Tests for Nova instance diagnostics (microversion v2.48)
Existing instance diagnostics tests were moved to the new files to test diagnostics for different microversions (2.1 and 2.48). Depends-On: If0b1493cc5c1c7f0d9896dd68342ad4dea4f7da2 Change-Id: I7757c5beeea3d3b0bc15a51cafc5ea2ada65e76c
This commit is contained in:
parent
8fde5658a8
commit
0cb4f2255c
@ -330,6 +330,10 @@ Microversion tests implemented in Tempest
|
|||||||
|
|
||||||
.. _2.47: http://docs.openstack.org/developer/nova/api_microversion_history.html#id42
|
.. _2.47: http://docs.openstack.org/developer/nova/api_microversion_history.html#id42
|
||||||
|
|
||||||
|
* `2.48`_
|
||||||
|
|
||||||
|
.. _2.48: http://docs.openstack.org/developer/nova/api_microversion_history.html#id43
|
||||||
|
|
||||||
* Volume
|
* Volume
|
||||||
|
|
||||||
* `3.3`_
|
* `3.3`_
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add validation schema for Nova server diagnostics API
|
76
tempest/api/compute/admin/test_server_diagnostics.py
Normal file
76
tempest/api/compute/admin/test_server_diagnostics.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Copyright 2017 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from tempest.api.compute import base
|
||||||
|
from tempest.lib import decorators
|
||||||
|
|
||||||
|
|
||||||
|
class ServerDiagnosticsTest(base.BaseV2ComputeAdminTest):
|
||||||
|
min_microversion = None
|
||||||
|
max_microversion = '2.47'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_clients(cls):
|
||||||
|
super(ServerDiagnosticsTest, cls).setup_clients()
|
||||||
|
cls.client = cls.os_admin.servers_client
|
||||||
|
|
||||||
|
@decorators.idempotent_id('31ff3486-b8a0-4f56-a6c0-aab460531db3')
|
||||||
|
def test_get_server_diagnostics(self):
|
||||||
|
server_id = self.create_test_server(wait_until='ACTIVE')['id']
|
||||||
|
diagnostics = self.client.show_server_diagnostics(server_id)
|
||||||
|
|
||||||
|
# NOTE(snikitin): Before microversion 2.48 response data from each
|
||||||
|
# hypervisor (libvirt, xen, vmware) was different. None of the fields
|
||||||
|
# were equal. As this test is common for libvirt, xen and vmware CI
|
||||||
|
# jobs we can't check any field in the response because all fields are
|
||||||
|
# different.
|
||||||
|
self.assertNotEmpty(diagnostics)
|
||||||
|
|
||||||
|
|
||||||
|
class ServerDiagnosticsV248Test(base.BaseV2ComputeAdminTest):
|
||||||
|
min_microversion = '2.48'
|
||||||
|
max_microversion = 'latest'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_clients(cls):
|
||||||
|
super(ServerDiagnosticsV248Test, cls).setup_clients()
|
||||||
|
cls.client = cls.os_admin.servers_client
|
||||||
|
|
||||||
|
@decorators.idempotent_id('64d0d48c-dff1-11e6-bf01-fe55135034f3')
|
||||||
|
def test_get_server_diagnostics(self):
|
||||||
|
server_id = self.create_test_server(wait_until='ACTIVE')['id']
|
||||||
|
# Response status and filed types will be checked by json schema
|
||||||
|
self.client.show_server_diagnostics(server_id)
|
||||||
|
|
||||||
|
# NOTE(snikitin): This is a special case for Xen hypervisor. In Xen
|
||||||
|
# case 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 methods
|
||||||
|
# which get diagnostics stats from Xen may raise exceptions or
|
||||||
|
# return `NaN` values. Such behavior must be handled correctly.
|
||||||
|
# Response must contain all diagnostics fields (may be with `None`
|
||||||
|
# values) and response status must be 200. Line above checks it by
|
||||||
|
# json schema.
|
||||||
|
time.sleep(10)
|
||||||
|
diagnostics = self.client.show_server_diagnostics(server_id)
|
||||||
|
|
||||||
|
# NOTE(snikitin): After 10 seconds diagnostics fields must contain
|
||||||
|
# not None values. But we will check only "memory_details.maximum"
|
||||||
|
# field because only this field meets all the following conditions:
|
||||||
|
# 1) This field may be unset because of Xen 5 seconds timeout.
|
||||||
|
# 2) This field is present in responses from all three supported
|
||||||
|
# hypervisors (libvirt, xen, vmware).
|
||||||
|
self.assertIsNotNone(diagnostics['memory_details']['maximum'])
|
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright 2017 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from tempest.api.compute import base
|
||||||
|
from tempest.lib import decorators
|
||||||
|
from tempest.lib import exceptions as lib_exc
|
||||||
|
|
||||||
|
|
||||||
|
class ServerDiagnosticsNegativeTest(base.BaseV2ComputeAdminTest):
|
||||||
|
min_microversion = None
|
||||||
|
max_microversion = '2.47'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_clients(cls):
|
||||||
|
super(ServerDiagnosticsNegativeTest, cls).setup_clients()
|
||||||
|
cls.client = cls.servers_client
|
||||||
|
|
||||||
|
@decorators.attr(type=['negative'])
|
||||||
|
@decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
|
||||||
|
def test_get_server_diagnostics_by_non_admin(self):
|
||||||
|
# Non-admin user cannot view server diagnostics according to policy
|
||||||
|
server_id = self.create_test_server(wait_until='ACTIVE')['id']
|
||||||
|
self.assertRaises(lib_exc.Forbidden,
|
||||||
|
self.client.show_server_diagnostics, server_id)
|
||||||
|
|
||||||
|
|
||||||
|
class ServerDiagnosticsNegativeV248Test(ServerDiagnosticsNegativeTest):
|
||||||
|
min_microversion = '2.48'
|
||||||
|
max_microversion = 'latest'
|
@ -164,17 +164,6 @@ class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
|
|||||||
server = self.client.show_server(self.s1_id)['server']
|
server = self.client.show_server(self.s1_id)['server']
|
||||||
self.assertEqual(server['status'], 'ACTIVE')
|
self.assertEqual(server['status'], 'ACTIVE')
|
||||||
|
|
||||||
@decorators.skip_because(bug="1240043")
|
|
||||||
@decorators.idempotent_id('31ff3486-b8a0-4f56-a6c0-aab460531db3')
|
|
||||||
def test_get_server_diagnostics_by_admin(self):
|
|
||||||
# Retrieve server diagnostics by admin user
|
|
||||||
diagnostic = self.client.show_server_diagnostics(self.s1_id)
|
|
||||||
basic_attrs = ['rx_packets', 'rx_errors', 'rx_drop',
|
|
||||||
'tx_packets', 'tx_errors', 'tx_drop',
|
|
||||||
'read_req', 'write_req', 'cpu', 'memory']
|
|
||||||
for key in basic_attrs:
|
|
||||||
self.assertIn(key, str(diagnostic.keys()))
|
|
||||||
|
|
||||||
@decorators.idempotent_id('682cb127-e5bb-4f53-87ce-cb9003604442')
|
@decorators.idempotent_id('682cb127-e5bb-4f53-87ce-cb9003604442')
|
||||||
def test_rebuild_server_in_error_state(self):
|
def test_rebuild_server_in_error_state(self):
|
||||||
# The server in error state should be rebuilt using the provided
|
# The server in error state should be rebuilt using the provided
|
||||||
|
@ -107,14 +107,6 @@ class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
|
|||||||
self.assertRaises(lib_exc.NotFound,
|
self.assertRaises(lib_exc.NotFound,
|
||||||
self.client.reset_state, '999', state='error')
|
self.client.reset_state, '999', state='error')
|
||||||
|
|
||||||
@decorators.attr(type=['negative'])
|
|
||||||
@decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
|
|
||||||
def test_get_server_diagnostics_by_non_admin(self):
|
|
||||||
# Non-admin user can not view server diagnostics according to policy
|
|
||||||
self.assertRaises(lib_exc.Forbidden,
|
|
||||||
self.non_adm_client.show_server_diagnostics,
|
|
||||||
self.s1_id)
|
|
||||||
|
|
||||||
@decorators.attr(type=['negative'])
|
@decorators.attr(type=['negative'])
|
||||||
@decorators.idempotent_id('46a4e1ca-87ae-4d28-987a-1b6b136a0221')
|
@decorators.idempotent_id('46a4e1ca-87ae-4d28-987a-1b6b136a0221')
|
||||||
def test_migrate_non_existent_server(self):
|
def test_migrate_non_existent_server(self):
|
||||||
|
@ -582,3 +582,10 @@ evacuate_server_with_admin_pass = {
|
|||||||
'required': ['adminPass']
|
'required': ['adminPass']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_server_diagnostics = {
|
||||||
|
'status_code': [200],
|
||||||
|
'response_body': {
|
||||||
|
'type': 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
115
tempest/lib/api_schema/response/compute/v2_48/servers.py
Normal file
115
tempest/lib/api_schema/response/compute/v2_48/servers.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Copyright 2017 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_47 import servers as servers247
|
||||||
|
|
||||||
|
|
||||||
|
show_server_diagnostics = {
|
||||||
|
'status_code': [200],
|
||||||
|
'response_body': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'state': {
|
||||||
|
'type': 'string', 'enum': [
|
||||||
|
'pending', 'running', 'paused', 'shutdown', 'crashed',
|
||||||
|
'suspended']
|
||||||
|
},
|
||||||
|
'driver': {
|
||||||
|
'type': 'string', 'enum': [
|
||||||
|
'libvirt', 'xenapi', 'vmwareapi', 'ironic', 'hyperv']
|
||||||
|
},
|
||||||
|
'hypervisor': {'type': ['string', 'null']},
|
||||||
|
'hypervisor_os': {'type': ['string', 'null']},
|
||||||
|
'uptime': {'type': ['integer', 'null']},
|
||||||
|
'config_drive': {'type': 'boolean'},
|
||||||
|
'num_cpus': {'type': 'integer'},
|
||||||
|
'num_nics': {'type': 'integer'},
|
||||||
|
'num_disks': {'type': 'integer'},
|
||||||
|
'memory_details': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'maximum': {'type': ['integer', 'null']},
|
||||||
|
'used': {'type': ['integer', 'null']}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['maximum', 'used']
|
||||||
|
},
|
||||||
|
'cpu_details': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'id': {'type': ['integer', 'null']},
|
||||||
|
'time': {'type': ['integer', 'null']},
|
||||||
|
'utilisation': {'type': ['integer', 'null']}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['id', 'time', 'utilisation']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'nic_details': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'mac_address': {'oneOf': [parameter_types.mac_address,
|
||||||
|
{'type': 'null'}]},
|
||||||
|
'rx_octets': {'type': ['integer', 'null']},
|
||||||
|
'rx_errors': {'type': ['integer', 'null']},
|
||||||
|
'rx_drop': {'type': ['integer', 'null']},
|
||||||
|
'rx_packets': {'type': ['integer', 'null']},
|
||||||
|
'rx_rate': {'type': ['integer', 'null']},
|
||||||
|
'tx_octets': {'type': ['integer', 'null']},
|
||||||
|
'tx_errors': {'type': ['integer', 'null']},
|
||||||
|
'tx_drop': {'type': ['integer', 'null']},
|
||||||
|
'tx_packets': {'type': ['integer', 'null']},
|
||||||
|
'tx_rate': {'type': ['integer', 'null']}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['mac_address', 'rx_octets', 'rx_errors',
|
||||||
|
'rx_drop',
|
||||||
|
'rx_packets', 'rx_rate', 'tx_octets',
|
||||||
|
'tx_errors',
|
||||||
|
'tx_drop', 'tx_packets', 'tx_rate']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'disk_details': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'read_bytes': {'type': ['integer', 'null']},
|
||||||
|
'read_requests': {'type': ['integer', 'null']},
|
||||||
|
'write_bytes': {'type': ['integer', 'null']},
|
||||||
|
'write_requests': {'type': ['integer', 'null']},
|
||||||
|
'errors_count': {'type': ['integer', 'null']}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['read_bytes', 'read_requests', 'write_bytes',
|
||||||
|
'write_requests', 'errors_count']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': [
|
||||||
|
'state', 'driver', 'hypervisor', 'hypervisor_os', 'uptime',
|
||||||
|
'config_drive', 'num_cpus', 'num_nics', 'num_disks',
|
||||||
|
'memory_details', 'cpu_details', 'nic_details', 'disk_details'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_server = copy.deepcopy(servers247.get_server)
|
@ -28,6 +28,7 @@ from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
|
|||||||
from tempest.lib.api_schema.response.compute.v2_26 import servers as schemav226
|
from tempest.lib.api_schema.response.compute.v2_26 import servers as schemav226
|
||||||
from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
|
from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
|
||||||
from tempest.lib.api_schema.response.compute.v2_47 import servers as schemav247
|
from tempest.lib.api_schema.response.compute.v2_47 import servers as schemav247
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_48 import servers as schemav248
|
||||||
from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
|
from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
|
||||||
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
|
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
|
||||||
from tempest.lib.common import rest_client
|
from tempest.lib.common import rest_client
|
||||||
@ -45,7 +46,8 @@ class ServersClient(base_compute_client.BaseComputeClient):
|
|||||||
{'min': '2.16', 'max': '2.18', 'schema': schemav216},
|
{'min': '2.16', 'max': '2.18', 'schema': schemav216},
|
||||||
{'min': '2.19', 'max': '2.25', 'schema': schemav219},
|
{'min': '2.19', 'max': '2.25', 'schema': schemav219},
|
||||||
{'min': '2.26', 'max': '2.46', 'schema': schemav226},
|
{'min': '2.26', 'max': '2.46', 'schema': schemav226},
|
||||||
{'min': '2.47', 'max': None, 'schema': schemav247}]
|
{'min': '2.47', 'max': '2.47', 'schema': schemav247},
|
||||||
|
{'min': '2.48', 'max': None, 'schema': schemav248}]
|
||||||
|
|
||||||
def __init__(self, auth_provider, service, region,
|
def __init__(self, auth_provider, service, region,
|
||||||
enable_instance_password=True, **kwargs):
|
enable_instance_password=True, **kwargs):
|
||||||
@ -658,7 +660,10 @@ class ServersClient(base_compute_client.BaseComputeClient):
|
|||||||
def show_server_diagnostics(self, server_id):
|
def show_server_diagnostics(self, server_id):
|
||||||
"""Get the usage data for a server."""
|
"""Get the usage data for a server."""
|
||||||
resp, body = self.get("servers/%s/diagnostics" % server_id)
|
resp, body = self.get("servers/%s/diagnostics" % server_id)
|
||||||
return rest_client.ResponseBody(resp, json.loads(body))
|
body = json.loads(body)
|
||||||
|
schema = self.get_schema(self.schema_versions_info)
|
||||||
|
self.validate_response(schema.show_server_diagnostics, resp, body)
|
||||||
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def list_instance_actions(self, server_id):
|
def list_instance_actions(self, server_id):
|
||||||
"""List the provided server action."""
|
"""List the provided server action."""
|
||||||
|
Loading…
Reference in New Issue
Block a user