Skip to content

Commit

Permalink
Merge "Completed implementation of instance diagnostics for Xen"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins authored and openstack-gerrit committed Jun 5, 2017
2 parents 78c69f6 + dcf9620 commit 58d0e58
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 26 deletions.
45 changes: 35 additions & 10 deletions nova/tests/unit/virt/xenapi/test_xenapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,26 +420,51 @@ def fake_get_rrd(host, vm_uuid):
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()
Expand Down
21 changes: 20 additions & 1 deletion nova/virt/xenapi/fake.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down Expand Up @@ -836,6 +842,19 @@ def VM_pause(self, session, vm_ref):
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

Expand Down
84 changes: 71 additions & 13 deletions nova/virt/xenapi/vm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"""

import contextlib
import math
import os
import time
import urllib
Expand Down Expand Up @@ -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)
Expand All @@ -1735,29 +1753,69 @@ 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)
_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)

for cpu_num in range(0, int(vm_rec['VCPUs_max'])):
diags.add_cpu()
return diags

for vif in vm_rec['VIFs']:
diags.add_nic()

for vbd in vm_rec['VBDs']:
diags.add_disk()
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)

max_mem_bytes = int(vm_rec['memory_dynamic_max'])
diags.memory_details = diagnostics.MemoryDiagnostics(
maximum=max_mem_bytes / units.Mi)

return diags
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):
Expand Down
4 changes: 2 additions & 2 deletions nova/virt/xenapi/vmops.py
Original file line number Diff line number Diff line change
Expand Up @@ -1776,8 +1776,8 @@ def get_diagnostics(self, instance):
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 = {}
Expand Down

0 comments on commit 58d0e58

Please sign in to comment.