Merge "Add new cpu_util meter recording CPU utilization %."
This commit is contained in:
commit
2206ed8ee7
@ -17,6 +17,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import datetime
|
||||||
|
import multiprocessing
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
@ -154,13 +156,46 @@ class CPUPollster(LibVirtPollster):
|
|||||||
|
|
||||||
LOG = log.getLogger(__name__ + '.cpu')
|
LOG = log.getLogger(__name__ + '.cpu')
|
||||||
|
|
||||||
|
utilization_map = {}
|
||||||
|
|
||||||
|
def get_cpu_util(self, instance, cpu_info):
|
||||||
|
prev_times = self.utilization_map.get(instance.uuid)
|
||||||
|
self.utilization_map[instance.uuid] = (cpu_info['cpu_time'],
|
||||||
|
datetime.datetime.now())
|
||||||
|
cpu_util = 0.0
|
||||||
|
if prev_times:
|
||||||
|
prev_cpu = prev_times[0]
|
||||||
|
prev_timestamp = prev_times[1]
|
||||||
|
delta = self.utilization_map[instance.uuid][1] - prev_timestamp
|
||||||
|
elapsed = (delta.seconds * (10 ** 6) + delta.microseconds) * 1000
|
||||||
|
cores_fraction = instance.vcpus * 1.0 / multiprocessing.cpu_count()
|
||||||
|
# account for cpu_time being reset when the instance is restarted
|
||||||
|
time_used = (cpu_info['cpu_time'] - prev_cpu
|
||||||
|
if prev_cpu <= cpu_info['cpu_time'] else
|
||||||
|
cpu_info['cpu_time'])
|
||||||
|
cpu_util = 100 * cores_fraction * time_used / elapsed
|
||||||
|
return cpu_util
|
||||||
|
|
||||||
def get_counters(self, manager, instance):
|
def get_counters(self, manager, instance):
|
||||||
conn = get_libvirt_connection()
|
conn = get_libvirt_connection()
|
||||||
self.LOG.info('checking instance %s', instance.uuid)
|
self.LOG.info('checking instance %s', instance.uuid)
|
||||||
try:
|
try:
|
||||||
cpu_info = conn.get_info(instance)
|
cpu_info = conn.get_info(instance)
|
||||||
self.LOG.info("CPUTIME USAGE: %s %d",
|
self.LOG.info("CPUTIME USAGE: %s %d",
|
||||||
instance, cpu_info['cpu_time'])
|
dict(instance), cpu_info['cpu_time'])
|
||||||
|
cpu_util = self.get_cpu_util(instance, cpu_info)
|
||||||
|
self.LOG.info("CPU UTILIZATION %%: %s %0.2f",
|
||||||
|
dict(instance), cpu_util)
|
||||||
|
# FIXME(eglynn): once we have a way of configuring which measures
|
||||||
|
# are published to each sink, we should by default
|
||||||
|
# disable publishing this derived measure to the
|
||||||
|
# metering store, only publishing to those sinks
|
||||||
|
# that specifically need it
|
||||||
|
yield make_counter_from_instance(instance,
|
||||||
|
name='cpu_util',
|
||||||
|
type=counter.TYPE_GAUGE,
|
||||||
|
volume=cpu_util,
|
||||||
|
)
|
||||||
yield make_counter_from_instance(instance,
|
yield make_counter_from_instance(instance,
|
||||||
name='cpu',
|
name='cpu',
|
||||||
type=counter.TYPE_CUMULATIVE,
|
type=counter.TYPE_CUMULATIVE,
|
||||||
|
@ -27,6 +27,7 @@ else:
|
|||||||
libvirt_missing = False
|
libvirt_missing = False
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import time
|
||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
|
|
||||||
@ -39,11 +40,12 @@ import mox
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def fake_libvirt_conn(moxobj):
|
def fake_libvirt_conn(moxobj, count=1):
|
||||||
conn = moxobj.CreateMockAnything()
|
conn = moxobj.CreateMockAnything()
|
||||||
conn._conn = moxobj.CreateMockAnything()
|
conn._conn = moxobj.CreateMockAnything()
|
||||||
moxobj.StubOutWithMock(libvirt, 'get_libvirt_connection')
|
moxobj.StubOutWithMock(libvirt, 'get_libvirt_connection')
|
||||||
libvirt.get_libvirt_connection().AndReturn(conn)
|
for _ in xrange(count):
|
||||||
|
libvirt.get_libvirt_connection().AndReturn(conn)
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
|
||||||
@ -210,13 +212,27 @@ class TestCPUPollster(TestLibvirtBase):
|
|||||||
self.pollster = libvirt.CPUPollster()
|
self.pollster = libvirt.CPUPollster()
|
||||||
|
|
||||||
def test_get_counter(self):
|
def test_get_counter(self):
|
||||||
expected_cpu_time = 12345
|
self.instance.vcpus = 1
|
||||||
|
conn = fake_libvirt_conn(self.mox, 3)
|
||||||
conn = fake_libvirt_conn(self.mox)
|
|
||||||
self.mox.StubOutWithMock(conn, 'get_info')
|
self.mox.StubOutWithMock(conn, 'get_info')
|
||||||
conn.get_info(self.instance).AndReturn({'cpu_time': expected_cpu_time})
|
conn.get_info(self.instance).AndReturn({'cpu_time': 1 * (10 ** 6)})
|
||||||
|
conn.get_info(self.instance).AndReturn({'cpu_time': 3 * (10 ** 6)})
|
||||||
|
# cpu_time resets on instance restart
|
||||||
|
conn.get_info(self.instance).AndReturn({'cpu_time': 2 * (10 ** 6)})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
counters = list(self.pollster.get_counters(self.manager,
|
|
||||||
self.instance))
|
def _verify_cpu_metering(zero, expected_time):
|
||||||
self.assertEquals(len(counters), 1)
|
counters = list(self.pollster.get_counters(self.manager,
|
||||||
assert counters[0].volume == expected_cpu_time
|
self.instance))
|
||||||
|
self.assertEquals(len(counters), 2)
|
||||||
|
assert counters[0].name == 'cpu_util'
|
||||||
|
assert (counters[0].volume == 0.0 if zero else
|
||||||
|
counters[0].volume > 0.0)
|
||||||
|
assert counters[1].name == 'cpu'
|
||||||
|
assert counters[1].volume == expected_time
|
||||||
|
# ensure elapsed time between polling cycles is non-zero
|
||||||
|
time.sleep(0.001)
|
||||||
|
|
||||||
|
_verify_cpu_metering(True, 1 * (10 ** 6))
|
||||||
|
_verify_cpu_metering(False, 3 * (10 ** 6))
|
||||||
|
_verify_cpu_metering(False, 2 * (10 ** 6))
|
||||||
|
Loading…
Reference in New Issue
Block a user