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:
parent
26a13b7875
commit
8b54c4a061
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -38,6 +38,7 @@ policy_data = """
|
|||
|
||||
"compute:get_instance_faults": "",
|
||||
"compute:get_diagnostics": "",
|
||||
"compute:get_instance_diagnostics": "",
|
||||
|
||||
"compute:get_lock": "",
|
||||
"compute:lock": "",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue