
Currently we are querying the latest real time sample from vSphere and creating a sample out of it, in ceilometer. The sampling interval of real time samples in vSphere is 20 s. However, the interval at which the various pollsters are run is by default 600 s (10 mins). This CL attempts to improve the accuracy of the sample by averaging the samples over the pollster interval. Change-Id: Id75fe87840b82d7c7d3e4fae9b2fc982e22ea1d7 Implements: blueprint vmware-vcenter-server Closes-Bug: 1295500
229 lines
9.9 KiB
Python
229 lines
9.9 KiB
Python
# Copyright (c) 2014 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.
|
|
|
|
from oslo.vmware import vim_util
|
|
|
|
|
|
PERF_MANAGER_TYPE = "PerformanceManager"
|
|
PERF_COUNTER_PROPERTY = "perfCounter"
|
|
VM_INSTANCE_ID_PROPERTY = 'config.extraConfig["nvp.vm-uuid"].value'
|
|
|
|
# ESXi Servers sample performance data every 20 seconds. 20-second interval
|
|
# data is called instance data or real-time data. To retrieve instance data,
|
|
# we need to specify a value of 20 seconds for the "PerfQuerySpec.intervalId"
|
|
# property. In that case the "QueryPerf" method operates as a raw data feed
|
|
# that bypasses the vCenter database and instead retrieves performance data
|
|
# from an ESXi host.
|
|
# The following value is time interval for real-time performance stats
|
|
# in seconds and it is not configurable.
|
|
VC_REAL_TIME_SAMPLING_INTERVAL = 20
|
|
|
|
|
|
class VsphereOperations(object):
|
|
"""Class to invoke vSphere APIs calls required by various
|
|
pollsters, collecting data from VMware infrastructure.
|
|
"""
|
|
def __init__(self, api_session, max_objects):
|
|
self._api_session = api_session
|
|
self._max_objects = max_objects
|
|
# Mapping between "VM's Nova instance Id" -> "VM's MOID"
|
|
# In case a VM is deployed by Nova, then its name is instance ID.
|
|
# So this map essentially has VM names as keys.
|
|
self._vm_moid_lookup_map = {}
|
|
|
|
# Mapping from full name -> ID, for VC Performance counters
|
|
self._perf_counter_id_lookup_map = None
|
|
|
|
def _init_vm_moid_lookup_map(self):
|
|
session = self._api_session
|
|
result = session.invoke_api(vim_util, "get_objects", session.vim,
|
|
"VirtualMachine", self._max_objects,
|
|
[VM_INSTANCE_ID_PROPERTY],
|
|
False)
|
|
while result:
|
|
for vm_object in result.objects:
|
|
vm_moid = vm_object.obj.value
|
|
# propSet will be set only if the server provides value
|
|
if hasattr(vm_object, 'propSet') and vm_object.propSet:
|
|
vm_instance_id = vm_object.propSet[0].val
|
|
if vm_instance_id:
|
|
self._vm_moid_lookup_map[vm_instance_id] = vm_moid
|
|
|
|
result = session.invoke_api(vim_util, "continue_retrieval",
|
|
session.vim, result)
|
|
|
|
def get_vm_moid(self, vm_instance_id):
|
|
"""Method returns VC MOID of the VM by its NOVA instance ID.
|
|
"""
|
|
if vm_instance_id not in self._vm_moid_lookup_map:
|
|
self._init_vm_moid_lookup_map()
|
|
|
|
return self._vm_moid_lookup_map.get(vm_instance_id, None)
|
|
|
|
def _init_perf_counter_id_lookup_map(self):
|
|
|
|
# Query details of all the performance counters from VC
|
|
session = self._api_session
|
|
client_factory = session.vim.client.factory
|
|
perf_manager = session.vim.service_content.perfManager
|
|
|
|
prop_spec = vim_util.build_property_spec(
|
|
client_factory, PERF_MANAGER_TYPE, [PERF_COUNTER_PROPERTY])
|
|
|
|
obj_spec = vim_util.build_object_spec(
|
|
client_factory, perf_manager, None)
|
|
|
|
filter_spec = vim_util.build_property_filter_spec(
|
|
client_factory, [prop_spec], [obj_spec])
|
|
|
|
options = client_factory.create('ns0:RetrieveOptions')
|
|
options.maxObjects = 1
|
|
|
|
prop_collector = session.vim.service_content.propertyCollector
|
|
result = session.invoke_api(session.vim, "RetrievePropertiesEx",
|
|
prop_collector, specSet=[filter_spec],
|
|
options=options)
|
|
|
|
perf_counter_infos = result.objects[0].propSet[0].val.PerfCounterInfo
|
|
|
|
# Extract the counter Id for each counter and populate the map
|
|
self._perf_counter_id_lookup_map = {}
|
|
for perf_counter_info in perf_counter_infos:
|
|
|
|
counter_group = perf_counter_info.groupInfo.key
|
|
counter_name = perf_counter_info.nameInfo.key
|
|
counter_rollup_type = perf_counter_info.rollupType
|
|
counter_id = perf_counter_info.key
|
|
|
|
counter_full_name = (counter_group + ":" + counter_name + ":" +
|
|
counter_rollup_type)
|
|
self._perf_counter_id_lookup_map[counter_full_name] = counter_id
|
|
|
|
def get_perf_counter_id(self, counter_full_name):
|
|
"""Method returns the ID of VC performance counter by its full name.
|
|
|
|
A VC performance counter is uniquely identified by the
|
|
tuple {'Group Name', 'Counter Name', 'Rollup Type'}.
|
|
It will have an id - counter ID (changes from one VC to another),
|
|
which is required to query performance stats from that VC.
|
|
This method returns the ID for a counter,
|
|
assuming 'CounterFullName' => 'Group Name:CounterName:RollupType'.
|
|
"""
|
|
if not self._perf_counter_id_lookup_map:
|
|
self._init_perf_counter_id_lookup_map()
|
|
return self._perf_counter_id_lookup_map[counter_full_name]
|
|
|
|
# TODO(akhils@vmware.com) Move this method to common library
|
|
# when it gets checked-in
|
|
def query_vm_property(self, vm_moid, property_name):
|
|
"""Method returns the value of specified property for a VM.
|
|
|
|
:param vm_moid: moid of the VM whose property is to be queried
|
|
:param property_name: path of the property
|
|
"""
|
|
vm_mobj = vim_util.get_moref(vm_moid, "VirtualMachine")
|
|
session = self._api_session
|
|
return session.invoke_api(vim_util, "get_object_property",
|
|
session.vim, vm_mobj, property_name)
|
|
|
|
def query_vm_aggregate_stats(self, vm_moid, counter_id, duration):
|
|
"""Method queries the aggregated real-time stat value for a VM.
|
|
|
|
This method should be used for aggregate counters.
|
|
|
|
:param vm_moid: moid of the VM
|
|
:param counter_id: id of the perf counter in VC
|
|
:param duration: in seconds from current time,
|
|
over which the stat value was applicable
|
|
:return: the aggregated stats value for the counter
|
|
"""
|
|
# For aggregate counters, device_name should be ""
|
|
stats = self._query_vm_perf_stats(vm_moid, counter_id, "", duration)
|
|
|
|
# Performance manager provides the aggregated stats value
|
|
# with device name -> None
|
|
return stats.get(None, 0)
|
|
|
|
def query_vm_device_stats(self, vm_moid, counter_id, duration):
|
|
"""Method queries the real-time stat values for a VM, for all devices.
|
|
|
|
This method should be used for device(non-aggregate) counters.
|
|
|
|
:param vm_moid: moid of the VM
|
|
:param counter_id: id of the perf counter in VC
|
|
:param duration: in seconds from current time,
|
|
over which the stat value was applicable
|
|
:return: a map containing the stat values keyed by the device ID/name
|
|
"""
|
|
# For device counters, device_name should be "*" to get stat values
|
|
# for all devices.
|
|
stats = self._query_vm_perf_stats(vm_moid, counter_id, "*", duration)
|
|
|
|
# For some device counters, in addition to the per device value
|
|
# the Performance manager also returns the aggregated value.
|
|
# Just to be consistent, deleting the aggregated value if present.
|
|
stats.pop(None, None)
|
|
return stats
|
|
|
|
def _query_vm_perf_stats(self, vm_moid, counter_id, device_name, duration):
|
|
"""Method queries the real-time stat values for a VM.
|
|
|
|
:param vm_moid: moid of the VM for which stats are needed
|
|
:param counter_id: id of the perf counter in VC
|
|
:param device_name: name of the device for which stats are to be
|
|
queried. For aggregate counters pass empty string ("").
|
|
For device counters pass "*", if stats are required over all
|
|
devices.
|
|
:param duration: in seconds from current time,
|
|
over which the stat value was applicable
|
|
:return: a map containing the stat values keyed by the device ID/name
|
|
"""
|
|
|
|
session = self._api_session
|
|
client_factory = session.vim.client.factory
|
|
|
|
# Construct the QuerySpec
|
|
metric_id = client_factory.create('ns0:PerfMetricId')
|
|
metric_id.counterId = counter_id
|
|
metric_id.instance = device_name
|
|
|
|
query_spec = client_factory.create('ns0:PerfQuerySpec')
|
|
query_spec.entity = vim_util.get_moref(vm_moid, "VirtualMachine")
|
|
query_spec.metricId = [metric_id]
|
|
query_spec.intervalId = VC_REAL_TIME_SAMPLING_INTERVAL
|
|
# We query all samples which are applicable over the specified duration
|
|
samples_cnt = (duration / VC_REAL_TIME_SAMPLING_INTERVAL if duration
|
|
else 1)
|
|
query_spec.maxSample = samples_cnt
|
|
|
|
perf_manager = session.vim.service_content.perfManager
|
|
perf_stats = session.invoke_api(session.vim, 'QueryPerf', perf_manager,
|
|
querySpec=[query_spec])
|
|
|
|
stat_values = {}
|
|
if perf_stats:
|
|
entity_metric = perf_stats[0]
|
|
sample_infos = entity_metric.sampleInfo
|
|
|
|
if len(sample_infos) > 0:
|
|
for metric_series in entity_metric.value:
|
|
# Take the average of all samples to improve the accuracy
|
|
# of the stat value
|
|
stat_value = float(sum(metric_series.value)) / samples_cnt
|
|
device_id = metric_series.id.instance
|
|
stat_values[device_id] = stat_value
|
|
|
|
return stat_values
|