Completed implementation of instance diagnostics for Xen
Added data about NICs, CPUs and disks. blueprint: restore-vm-diagnostics Change-Id: I3ab0c843f626951ce422ff530b5bc2d266a3d9a2
This commit is contained in:
parent
0072f70d9b
commit
dcf962069c
|
@ -420,26 +420,51 @@ class XenAPIVMTestCase(stubs.XenAPITestBase,
|
|||
self.assertThat(actual, matchers.DictMatches(expected))
|
||||
|
||||
def test_get_instance_diagnostics(self):
|
||||
def fake_get_rrd(host, vm_uuid):
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(os.path.join(path, 'vm_rrd.xml')) as f:
|
||||
return re.sub(r'\s', '', f.read())
|
||||
self.stubs.Set(vm_utils, '_get_rrd', fake_get_rrd)
|
||||
|
||||
expected = fake_diagnostics.fake_diagnostics_obj(
|
||||
config_drive=False,
|
||||
state='running',
|
||||
driver='xenapi',
|
||||
cpu_details=[{}, {}, {}, {}], # 4 CPUs with 'None' values
|
||||
nic_details=[{}], # 1 NIC with 'None' values
|
||||
disk_details=[{}], # 1 disk with 'None' values
|
||||
memory_details={'maximum': 8192})
|
||||
cpu_details=[{'id': 0, 'utilisation': 11},
|
||||
{'id': 1, 'utilisation': 22},
|
||||
{'id': 2, 'utilisation': 33},
|
||||
{'id': 3, 'utilisation': 44}],
|
||||
nic_details=[{'mac_address': 'DE:AD:BE:EF:00:01',
|
||||
'rx_rate': 50,
|
||||
'tx_rate': 100}],
|
||||
disk_details=[{'read_bytes': 50, 'write_bytes': 100}],
|
||||
memory_details={'maximum': 8192, 'used': 3072})
|
||||
|
||||
instance = self._create_instance(obj=True)
|
||||
actual = self.conn.get_instance_diagnostics(instance)
|
||||
|
||||
self.assertDiagnosticsEqual(expected, actual)
|
||||
|
||||
def _test_get_instance_diagnostics_failure(self, **kwargs):
|
||||
instance = self._create_instance(obj=True)
|
||||
|
||||
with mock.patch.object(xenapi_fake.SessionBase, 'VM_query_data_source',
|
||||
**kwargs):
|
||||
actual = self.conn.get_instance_diagnostics(instance)
|
||||
|
||||
expected = fake_diagnostics.fake_diagnostics_obj(
|
||||
config_drive=False,
|
||||
state='running',
|
||||
driver='xenapi',
|
||||
cpu_details=[{'id': 0}, {'id': 1}, {'id': 2}, {'id': 3}],
|
||||
nic_details=[{'mac_address': 'DE:AD:BE:EF:00:01'}],
|
||||
disk_details=[{}],
|
||||
memory_details={'maximum': None, 'used': None})
|
||||
|
||||
self.assertDiagnosticsEqual(expected, actual)
|
||||
|
||||
def test_get_instance_diagnostics_xenapi_exception(self):
|
||||
self._test_get_instance_diagnostics_failure(
|
||||
side_effect=XenAPI.Failure(''))
|
||||
|
||||
def test_get_instance_diagnostics_nan_value(self):
|
||||
self._test_get_instance_diagnostics_failure(
|
||||
return_value=float('NaN'))
|
||||
|
||||
def test_get_vnc_console(self):
|
||||
instance = self._create_instance(obj=True)
|
||||
session = get_session()
|
||||
|
|
|
@ -236,7 +236,13 @@ def after_VBD_create(vbd_ref, vbd_rec):
|
|||
is created.
|
||||
"""
|
||||
vbd_rec['currently_attached'] = False
|
||||
vbd_rec['device'] = ''
|
||||
|
||||
# TODO(snikitin): Find a better way for generating of device name.
|
||||
# Usually 'userdevice' has numeric values like '1', '2', '3', etc.
|
||||
# Ideally they should be transformed to something like 'xvda', 'xvdb',
|
||||
# 'xvdx', etc. But 'userdevice' also may be 'autodetect', 'fake' or even
|
||||
# unset. We should handle it in future.
|
||||
vbd_rec['device'] = vbd_rec.get('userdevice', '')
|
||||
vbd_rec.setdefault('other_config', {})
|
||||
|
||||
vm_ref = vbd_rec['VM']
|
||||
|
@ -836,6 +842,19 @@ class SessionBase(object):
|
|||
db_ref = _db_content['VM'][vm_ref]
|
||||
db_ref['power_state'] = 'Paused'
|
||||
|
||||
def VM_query_data_source(self, session, vm_ref, field):
|
||||
vm = {'cpu0': 0.11,
|
||||
'cpu1': 0.22,
|
||||
'cpu2': 0.33,
|
||||
'cpu3': 0.44,
|
||||
'memory': 8 * units.Gi, # 8GB in bytes
|
||||
'memory_internal_free': 5 * units.Mi, # 5GB in kilobytes
|
||||
'vif_0_rx': 50,
|
||||
'vif_0_tx': 100,
|
||||
'vbd_0_read': 50,
|
||||
'vbd_0_write': 100}
|
||||
return vm.get(field, 0)
|
||||
|
||||
def pool_eject(self, session, host_ref):
|
||||
pass
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ their attributes like VDIs, VIFs, as well as their lookup functions.
|
|||
"""
|
||||
|
||||
import contextlib
|
||||
import math
|
||||
import os
|
||||
import time
|
||||
import urllib
|
||||
|
@ -1722,6 +1723,23 @@ def get_power_state(session, vm_ref):
|
|||
return XENAPI_POWER_STATE[xapi_state]
|
||||
|
||||
|
||||
def _vm_query_data_source(session, *args):
|
||||
"""We're getting diagnostics stats from the RRDs which are updated every
|
||||
5 seconds. It means that diagnostics information may be incomplete during
|
||||
first 5 seconds of VM life. In such cases method ``query_data_source()``
|
||||
may raise a ``XenAPI.Failure`` exception or may return a `NaN` value.
|
||||
"""
|
||||
|
||||
try:
|
||||
value = session.VM.query_data_source(*args)
|
||||
except session.XenAPI.Failure:
|
||||
return None
|
||||
|
||||
if math.isnan(value):
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def compile_info(session, vm_ref):
|
||||
"""Fill record with VM status information."""
|
||||
power_state = get_power_state(session, vm_ref)
|
||||
|
@ -1735,31 +1753,71 @@ def compile_info(session, vm_ref):
|
|||
num_cpu=num_cpu)
|
||||
|
||||
|
||||
def compile_instance_diagnostics(instance, vm_rec):
|
||||
vm_power_state_int = XENAPI_POWER_STATE[vm_rec['power_state']]
|
||||
vm_power_state = power_state.STATE_MAP[vm_power_state_int]
|
||||
def compile_instance_diagnostics(session, instance, vm_ref):
|
||||
xen_power_state = session.VM.get_power_state(vm_ref)
|
||||
vm_power_state = power_state.STATE_MAP[XENAPI_POWER_STATE[xen_power_state]]
|
||||
config_drive = configdrive.required_by(instance)
|
||||
|
||||
diags = diagnostics.Diagnostics(state=vm_power_state,
|
||||
driver='xenapi',
|
||||
config_drive=config_drive)
|
||||
|
||||
for cpu_num in range(0, int(vm_rec['VCPUs_max'])):
|
||||
diags.add_cpu()
|
||||
|
||||
for vif in vm_rec['VIFs']:
|
||||
diags.add_nic()
|
||||
|
||||
for vbd in vm_rec['VBDs']:
|
||||
diags.add_disk()
|
||||
|
||||
max_mem_bytes = int(vm_rec['memory_dynamic_max'])
|
||||
diags.memory_details = diagnostics.MemoryDiagnostics(
|
||||
maximum=max_mem_bytes / units.Mi)
|
||||
_add_cpu_usage(session, vm_ref, diags)
|
||||
_add_nic_usage(session, vm_ref, diags)
|
||||
_add_disk_usage(session, vm_ref, diags)
|
||||
_add_memory_usage(session, vm_ref, diags)
|
||||
|
||||
return diags
|
||||
|
||||
|
||||
def _add_cpu_usage(session, vm_ref, diag_obj):
|
||||
cpu_num = int(session.VM.get_VCPUs_max(vm_ref))
|
||||
for cpu_num in range(0, cpu_num):
|
||||
utilisation = _vm_query_data_source(session, vm_ref, "cpu%d" % cpu_num)
|
||||
if utilisation is not None:
|
||||
utilisation *= 100
|
||||
diag_obj.add_cpu(id=cpu_num, utilisation=utilisation)
|
||||
|
||||
|
||||
def _add_nic_usage(session, vm_ref, diag_obj):
|
||||
vif_refs = session.VM.get_VIFs(vm_ref)
|
||||
for vif_ref in vif_refs:
|
||||
vif_rec = session.VIF.get_record(vif_ref)
|
||||
rx_rate = _vm_query_data_source(session, vm_ref,
|
||||
"vif_%s_rx" % vif_rec['device'])
|
||||
tx_rate = _vm_query_data_source(session, vm_ref,
|
||||
"vif_%s_tx" % vif_rec['device'])
|
||||
diag_obj.add_nic(mac_address=vif_rec['MAC'],
|
||||
rx_rate=rx_rate,
|
||||
tx_rate=tx_rate)
|
||||
|
||||
|
||||
def _add_disk_usage(session, vm_ref, diag_obj):
|
||||
vbd_refs = session.VM.get_VBDs(vm_ref)
|
||||
for vbd_ref in vbd_refs:
|
||||
vbd_rec = session.VBD.get_record(vbd_ref)
|
||||
read_bytes = _vm_query_data_source(session, vm_ref,
|
||||
"vbd_%s_read" % vbd_rec['device'])
|
||||
write_bytes = _vm_query_data_source(session, vm_ref,
|
||||
"vbd_%s_write" % vbd_rec['device'])
|
||||
diag_obj.add_disk(read_bytes=read_bytes, write_bytes=write_bytes)
|
||||
|
||||
|
||||
def _add_memory_usage(session, vm_ref, diag_obj):
|
||||
total_mem = _vm_query_data_source(session, vm_ref, "memory")
|
||||
free_mem = _vm_query_data_source(session, vm_ref, "memory_internal_free")
|
||||
used_mem = None
|
||||
if total_mem is not None:
|
||||
# total_mem provided from XenServer is in Bytes. Converting it to MB.
|
||||
total_mem /= units.Mi
|
||||
|
||||
if free_mem is not None:
|
||||
# free_mem provided from XenServer is in KB. Converting it to MB.
|
||||
used_mem = total_mem - free_mem / units.Ki
|
||||
|
||||
diag_obj.memory_details = diagnostics.MemoryDiagnostics(
|
||||
maximum=total_mem, used=used_mem)
|
||||
|
||||
|
||||
def compile_diagnostics(vm_rec):
|
||||
"""Compile VM diagnostics data."""
|
||||
try:
|
||||
|
|
|
@ -1776,8 +1776,8 @@ class VMOps(object):
|
|||
def get_instance_diagnostics(self, instance):
|
||||
"""Return data about VM diagnostics using the common API."""
|
||||
vm_ref = self._get_vm_opaque_ref(instance)
|
||||
vm_rec = self._session.VM.get_record(vm_ref)
|
||||
return vm_utils.compile_instance_diagnostics(instance, vm_rec)
|
||||
return vm_utils.compile_instance_diagnostics(self._session, instance,
|
||||
vm_ref)
|
||||
|
||||
def _get_vif_device_map(self, vm_rec):
|
||||
vif_map = {}
|
||||
|
|
Loading…
Reference in New Issue