VM diagnostics (v3 API only)

There is no formal definition for the VM diagnostics. For the
V2 API the diagnostics will be returned as they are today.

This will continue to use the driver function get_diagnostics.

This change will be implemented by a new virt driver method called
get_instance_diagnostics.

This will support backward compatibility with the existing API's.

Part of the blueprint v3-diagnostics

This patch set is for the API's. Virt driver support in additional
patches.

DocImpact

Change-Id: Id722f38fb134b322413d2b5c3459c8fc0af7bd36
This commit is contained in:
Gary Kotton 2013-12-12 06:20:53 -08:00
parent 26a13b7875
commit 8b54c4a061
14 changed files with 251 additions and 42 deletions

View File

@ -1,17 +1,40 @@
{
"cpu0_time": 17300000000,
"memory": 524288,
"vda_errors": -1,
"vda_read": 262144,
"vda_read_req": 112,
"vda_write": 5778432,
"vda_write_req": 488,
"vnet1_rx": 2070139,
"vnet1_rx_drop": 0,
"vnet1_rx_errors": 0,
"vnet1_rx_packets": 26701,
"vnet1_tx": 140208,
"vnet1_tx_drop": 0,
"vnet1_tx_errors": 0,
"vnet1_tx_packets": 662
}
"config_drive": 1,
"cpu_details": [
{
"time": 17300000000
}
],
"disk_details": [
{
"errors_count": 0,
"id": "fake-disk-id",
"read_requests": 112,
"read_bytes": 262144,
"write_requests": 488,
"write_bytes": 5778432
}
],
"driver": "fake",
"hypervisor_os": "fake-os",
"memory_details": {
"maximum": 524288,
"used": 0
},
"nic_details": [
{
"mac_address": "01:23:45:67:89:ab",
"rx_drop": 0,
"rx_errors": 0,
"rx_octets": 2070139,
"rx_packets": 26701,
"tx_drop": 0,
"tx_errors": 0,
"tx_octets": 140208,
"tx_packets": 662
}
],
"state": "running",
"uptime": 46664,
"version": "1.0"
}

View File

@ -37,7 +37,7 @@ class ServerDiagnosticsController(object):
raise webob.exc.HTTPNotFound(explanation=e.format_message())
try:
return compute_api.get_diagnostics(context, instance)
return compute_api.get_instance_diagnostics(context, instance)
except exception.InstanceInvalidState as state_error:
common.raise_http_conflict_for_instance_invalid_state(state_error,
'get_diagnostics')

View File

@ -2535,6 +2535,12 @@ class API(base.Base):
"""Retrieve diagnostics for the given instance."""
return self.compute_rpcapi.get_diagnostics(context, instance=instance)
@wrap_check_policy
def get_instance_diagnostics(self, context, instance):
"""Retrieve diagnostics for the given instance."""
return self.compute_rpcapi.get_instance_diagnostics(context,
instance=instance)
@wrap_check_policy
@check_instance_lock
@check_instance_cell

View File

@ -290,6 +290,15 @@ class ComputeCellsAPI(compute_api.API):
super(ComputeCellsAPI, self).get_diagnostics(context, instance)
return self._call_to_cells(context, instance, 'get_diagnostics')
def get_instance_diagnostics(self, context, instance):
"""Retrieve diagnostics for the given instance."""
# FIXME(comstud): Cache this?
# Also: only calling super() to get state/policy checking
super(ComputeCellsAPI, self).get_instance_diagnostics(context,
instance)
return self._call_to_cells(context, instance,
'get_instance_diagnostics')
@check_instance_cell
def rescue(self, context, instance, rescue_password=None,
rescue_image_ref=None):

View File

@ -552,7 +552,7 @@ class ComputeVirtAPI(virtapi.VirtAPI):
class ComputeManager(manager.Manager):
"""Manages the running instances from creation to destruction."""
target = messaging.Target(version='3.30')
target = messaging.Target(version='3.31')
def __init__(self, compute_driver=None, *args, **kwargs):
"""Load configuration options and connect to the hypervisor."""
@ -3790,6 +3790,24 @@ class ComputeManager(manager.Manager):
state=instance.power_state,
method='get_diagnostics')
@object_compat
@wrap_exception()
@wrap_instance_fault
def get_instance_diagnostics(self, context, instance):
"""Retrieve diagnostics for an instance on this host."""
current_power_state = self._get_power_state(context, instance)
if current_power_state == power_state.RUNNING:
LOG.audit(_("Retrieving diagnostics"), context=context,
instance=instance)
diags = self.driver.get_instance_diagnostics(instance)
return diags.serialize()
else:
raise exception.InstanceInvalidState(
attr='power_state',
instance_uuid=instance.uuid,
state=instance.power_state,
method='get_diagnostics')
@wrap_exception()
@reverts_task_state
@wrap_instance_event

View File

@ -257,6 +257,7 @@ class ComputeAPI(object):
3.28 - Update get_console_output() to accept a new-world object
3.29 - Make check_instance_shared_storage accept a new-world object
3.30 - Make remove_volume_connection() accept a new-world object
3.31 - Add get_instance_diagnostics
'''
VERSION_ALIASES = {
@ -487,6 +488,14 @@ class ComputeAPI(object):
version=version)
return cctxt.call(ctxt, 'get_diagnostics', instance=instance)
def get_instance_diagnostics(self, ctxt, instance):
instance_p = jsonutils.to_primitive(instance)
kwargs = {'instance': instance_p}
version = '3.31'
cctxt = self.client.prepare(server=_compute_host(None, instance),
version=version)
return cctxt.call(ctxt, 'get_instance_diagnostics', **kwargs)
def get_vnc_console(self, ctxt, instance, console_type):
if self.client.can_send_version('3.2'):
version = '3.2'

View File

@ -26,8 +26,26 @@ from nova.tests.api.openstack import fakes
UUID = 'abc'
def fake_get_diagnostics(self, _context, instance_uuid):
return {'data': 'Some diagnostic info'}
def fake_get_instance_diagnostics(self, _context, instance_uuid):
return {'state': 'running',
'driver': 'fake',
'uptime': 7,
'cpu_details': [{'time': 1024}],
'nic_details': [{'rx_octets': 0,
'rx_errors': 0,
'rx_drop': 0,
'rx_packets': 0,
'tx_octets': 0,
'tx_errors': 0,
'tx_drop': 0,
'tx_packets': 0}],
'disk_details': [{'read_bytes': 0,
'read_requests': 0,
'write_bytes': 0,
'write_requests': 0,
'errors': 0}],
'memory_details': {'maximum': 512, 'used': 256},
'version': '1.0'}
def fake_instance_get(self, _context, instance_uuid, want_objects=False):
@ -43,18 +61,37 @@ class ServerDiagnosticsTest(test.NoDBTestCase):
self.router = compute.APIRouterV3(init_only=('servers',
'os-server-diagnostics'))
@mock.patch.object(compute_api.API, 'get_diagnostics',
fake_get_diagnostics)
@mock.patch.object(compute_api.API, 'get_instance_diagnostics',
fake_get_instance_diagnostics)
@mock.patch.object(compute_api.API, 'get', fake_instance_get)
def test_get_diagnostics(self):
req = fakes.HTTPRequestV3.blank(
'/servers/%s/os-server-diagnostics' % UUID)
res = req.get_response(self.router)
output = jsonutils.loads(res.body)
self.assertEqual(output, {'data': 'Some diagnostic info'})
expected = {'state': 'running',
'driver': 'fake',
'uptime': 7,
'cpu_details': [{'time': 1024}],
'nic_details': [{'rx_octets': 0,
'rx_errors': 0,
'rx_drop': 0,
'rx_packets': 0,
'tx_octets': 0,
'tx_errors': 0,
'tx_drop': 0,
'tx_packets': 0}],
'disk_details': [{'read_bytes': 0,
'read_requests': 0,
'write_bytes': 0,
'write_requests': 0,
'errors': 0}],
'memory_details': {'maximum': 512, 'used': 256},
'version': '1.0'}
self.assertEqual(expected, output)
@mock.patch.object(compute_api.API, 'get_diagnostics',
fake_get_diagnostics)
@mock.patch.object(compute_api.API, 'get_instance_diagnostics',
fake_get_instance_diagnostics)
@mock.patch.object(compute_api.API, 'get',
side_effect=exception.InstanceNotFound(instance_id=UUID))
def test_get_diagnostics_with_non_existed_instance(self, mock_get):
@ -63,7 +100,7 @@ class ServerDiagnosticsTest(test.NoDBTestCase):
res = req.get_response(self.router)
self.assertEqual(res.status_int, 404)
@mock.patch.object(compute_api.API, 'get_diagnostics',
@mock.patch.object(compute_api.API, 'get_instance_diagnostics',
side_effect=exception.InstanceInvalidState('fake message'))
@mock.patch.object(compute_api.API, 'get', fake_instance_get)
def test_get_diagnostics_raise_conflict_on_invalid_state(self,

View File

@ -3364,6 +3364,41 @@ class ComputeTestCase(BaseTestCase):
self.assertEqual(diagnostics, expected_diagnostic)
self.compute.terminate_instance(self.context, instance, [], [])
def test_instance_diagnostics(self):
# Make sure we can get diagnostics for an instance.
instance = jsonutils.to_primitive(self._create_fake_instance())
self.compute.run_instance(self.context, instance, {}, {}, [], None,
None, True, None, False)
diagnostics = self.compute.get_instance_diagnostics(self.context,
instance=instance)
expected = {'config_drive': True,
'cpu_details': [{'time': 17300000000}],
'disk_details': [{'errors_count': 0,
'id': 'fake-disk-id',
'read_bytes': 262144,
'read_requests': 112,
'write_bytes': 5778432,
'write_requests': 488}],
'driver': 'fake',
'hypervisor_os': 'fake-os',
'memory_details': {'maximum': 524288, 'used': 0},
'nic_details': [{'mac_address': '01:23:45:67:89:ab',
'rx_drop': 0,
'rx_errors': 0,
'rx_octets': 2070139,
'rx_packets': 26701,
'tx_drop': 0,
'tx_errors': 0,
'tx_octets': 140208,
'tx_packets': 662}],
'state': 'running',
'uptime': 46664,
'version': '1.0'}
self.assertEqual(expected, diagnostics)
self.compute.terminate_instance(self.context,
self._objectify(instance), [], [])
def test_add_fixed_ip_usage_notification(self):
def dummy(*args, **kwargs):
pass
@ -8995,6 +9030,20 @@ class ComputeAPITestCase(BaseTestCase):
lambda *a, **kw: None)
self.compute_api.delete(self.context, self._objectify(instance))
def test_get_instance_diagnostics(self):
instance = self._create_fake_instance()
rpcapi = compute_rpcapi.ComputeAPI
self.mox.StubOutWithMock(rpcapi, 'get_instance_diagnostics')
rpcapi.get_instance_diagnostics(self.context, instance=instance)
self.mox.ReplayAll()
self.compute_api.get_instance_diagnostics(self.context, instance)
self.stubs.Set(self.compute_api.network_api, 'deallocate_for_instance',
lambda *a, **kw: None)
self.compute_api.delete(self.context, self._objectify(instance))
def test_secgroup_refresh(self):
instance = self._create_fake_instance()

View File

@ -289,6 +289,10 @@ class ComputeRpcAPITestCase(test.TestCase):
self._test_compute_api('get_diagnostics', 'call',
instance=self.fake_instance, version='2.0')
def test_get_instance_diagnostics(self):
self._test_compute_api('get_instance_diagnostics', 'call',
instance=self.fake_instance, version='3.31')
def test_get_vnc_console(self):
self._test_compute_api('get_vnc_console', 'call',
instance=self.fake_instance_obj, console_type='type',

View File

@ -38,6 +38,7 @@ policy_data = """
"compute:get_instance_faults": "",
"compute:get_diagnostics": "",
"compute:get_instance_diagnostics": "",
"compute:get_lock": "",
"compute:lock": "",

View File

@ -1,17 +1,40 @@
{
"cpu0_time": 17300000000,
"memory": 524288,
"vda_errors": -1,
"vda_read": 262144,
"vda_read_req": 112,
"vda_write": 5778432,
"vda_write_req": 488,
"vnet1_rx": 2070139,
"vnet1_rx_drop": 0,
"vnet1_rx_errors": 0,
"vnet1_rx_packets": 26701,
"vnet1_tx": 140208,
"vnet1_tx_drop": 0,
"vnet1_tx_errors": 0,
"vnet1_tx_packets": 662
}
"config_drive": 1,
"cpu_details": [
{
"time": 17300000000
}
],
"disk_details": [
{
"errors_count": 0,
"id": "fake-disk-id",
"read_requests": 112,
"read_bytes": 262144,
"write_requests": 488,
"write_bytes": 5778432
}
],
"driver": "fake",
"hypervisor_os": "fake-os",
"memory_details": {
"maximum": 524288,
"used": 0
},
"nic_details": [
{
"mac_address": "01:23:45:67:89:ab",
"rx_drop": 0,
"rx_errors": 0,
"rx_octets": 2070139,
"rx_packets": 26701,
"tx_drop": 0,
"tx_errors": 0,
"tx_octets": 140208,
"tx_packets": 662
}
],
"state": "running",
"uptime": 46664,
"version": "1.0"
}

View File

@ -490,6 +490,11 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
instance_ref, network_info = self._get_running_instance(obj=True)
self.connection.get_diagnostics(instance_ref)
@catch_notimplementederror
def test_get_instance_diagnostics(self):
instance_ref, network_info = self._get_running_instance(obj=True)
self.connection.get_instance_diagnostics(instance_ref)
@catch_notimplementederror
def test_block_stats(self):
instance_ref, network_info = self._get_running_instance()

View File

@ -399,6 +399,13 @@ class ComputeDriver(object):
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
def get_instance_diagnostics(self, instance):
"""Return data about VM diagnostics.
:param instance: nova.objects.instance.Instance
"""
raise NotImplementedError()
def get_all_bw_counters(self, instances):
"""Return bandwidth usage counters for each interface on each
running VM.

View File

@ -35,6 +35,7 @@ from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt import diagnostics
from nova.virt import driver
from nova.virt import virtapi
@ -287,6 +288,23 @@ class FakeDriver(driver.ComputeDriver):
'vnet1_tx_packets': 662,
}
def get_instance_diagnostics(self, instance_name):
diags = diagnostics.Diagnostics(state='running', driver='fake',
hypervisor_os='fake-os', uptime=46664, config_drive=True)
diags.add_cpu(time=17300000000)
diags.add_nic(mac_address='01:23:45:67:89:ab',
rx_packets=26701,
rx_octets=2070139,
tx_octets=140208,
tx_packets = 662)
diags.add_disk(id='fake-disk-id',
read_bytes=262144,
read_requests=112,
write_bytes=5778432,
write_requests=488)
diags.memory_details.maximum = 524288
return diags
def get_all_bw_counters(self, instances):
"""Return bandwidth usage counters for each interface on each
running VM.