207 lines
8.7 KiB
Python
207 lines
8.7 KiB
Python
# 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 mock
|
|
|
|
from nova import test
|
|
from nova.virt.xenapi import host
|
|
|
|
|
|
class VGPUTestCase(test.NoDBTestCase):
|
|
"""Unit tests for Driver operations."""
|
|
@mock.patch.object(host.HostState, 'update_status',
|
|
return_value='fake_stats_1')
|
|
@mock.patch.object(host.HostState, '_get_vgpu_stats_in_group')
|
|
def test_get_vgpu_stats_empty_cfg(self, mock_get, mock_update):
|
|
# no vGPU type configured.
|
|
self.flags(enabled_vgpu_types=[], group='devices')
|
|
session = mock.Mock()
|
|
|
|
host_obj = host.HostState(session)
|
|
stats = host_obj._get_vgpu_stats()
|
|
|
|
session.call_xenapi.assert_not_called()
|
|
self.assertEqual(stats, {})
|
|
|
|
@mock.patch.object(host.HostState, 'update_status',
|
|
return_value='fake_stats_1')
|
|
@mock.patch.object(host.HostState, '_get_vgpu_stats_in_group')
|
|
def test_get_vgpu_stats_single_type(self, mock_get, mock_update):
|
|
# configured single vGPU type
|
|
self.flags(enabled_vgpu_types=['type_name_1'], group='devices')
|
|
session = mock.Mock()
|
|
# multiple GPU groups
|
|
session.call_xenapi.side_effect = [
|
|
['grp_ref1', 'grp_ref2'], # GPU_group.get_all
|
|
'uuid_1', # GPU_group.get_uuid
|
|
'uuid_2', # GPU_group.get_uuid
|
|
]
|
|
# Let it return None for the 2nd GPU group for the case
|
|
# that it doesn't have the specified vGPU type enabled.
|
|
mock_get.side_effect = ['fake_stats_1', None]
|
|
host_obj = host.HostState(session)
|
|
stats = host_obj._get_vgpu_stats()
|
|
|
|
self.assertEqual(session.call_xenapi.call_count, 3)
|
|
self.assertEqual(mock_update.call_count, 1)
|
|
self.assertEqual(mock_get.call_count, 2)
|
|
self.assertEqual(stats, {'uuid_1': 'fake_stats_1'})
|
|
|
|
@mock.patch.object(host.HostState, 'update_status',
|
|
return_value='fake_stats_1')
|
|
@mock.patch.object(host.HostState, '_get_vgpu_stats_in_group')
|
|
def test_get_vgpu_stats_multi_types(self, mock_get, mock_update):
|
|
# when multiple vGPU types configured, it use the first one.
|
|
self.flags(enabled_vgpu_types=['type_name_1', 'type_name_2'],
|
|
group='devices')
|
|
session = mock.Mock()
|
|
session.call_xenapi.side_effect = [
|
|
['grp_ref1'], # GPU_group.get_all
|
|
'uuid_1', # GPU_group.get_uuid
|
|
]
|
|
mock_get.side_effect = ['fake_stats_1']
|
|
host_obj = host.HostState(session)
|
|
stats = host_obj._get_vgpu_stats()
|
|
|
|
self.assertEqual(session.call_xenapi.call_count, 2)
|
|
self.assertEqual(mock_update.call_count, 1)
|
|
self.assertEqual(stats, {'uuid_1': 'fake_stats_1'})
|
|
# called with the first vGPU type: 'type_name_1'
|
|
mock_get.assert_called_with('grp_ref1', ['type_name_1'])
|
|
|
|
@mock.patch.object(host.HostState, 'update_status',
|
|
return_value='fake_stats_1')
|
|
@mock.patch.object(host.HostState, '_get_total_vgpu_in_grp',
|
|
return_value=7)
|
|
def test_get_vgpu_stats_in_group(self, mock_get, mock_update):
|
|
# Test it will return vGPU stat for the enabled vGPU type.
|
|
enabled_vgpu_types = ['type_name_2']
|
|
session = mock.Mock()
|
|
session.call_xenapi.side_effect = [
|
|
['type_ref_1', 'type_ref_2'], # GPU_group.get_enabled_VGPU_types
|
|
'type_name_1', # VGPU_type.get_model_name
|
|
'type_name_2', # VGPU_type.get_model_name
|
|
'type_uuid_2', # VGPU_type.get_uuid
|
|
'4', # VGPU_type.get_max_heads
|
|
'6', # GPU_group.get_remaining_capacity
|
|
]
|
|
host_obj = host.HostState(session)
|
|
|
|
stats = host_obj._get_vgpu_stats_in_group('grp_ref',
|
|
enabled_vgpu_types)
|
|
|
|
expect_stats = {'uuid': 'type_uuid_2',
|
|
'type_name': 'type_name_2',
|
|
'max_heads': 4,
|
|
'total': 7,
|
|
'remaining': 6,
|
|
}
|
|
self.assertEqual(session.call_xenapi.call_count, 6)
|
|
# It should get_uuid for the vGPU type passed via *enabled_vgpu_types*
|
|
# (the arg for get_uuid should be 'type_ref_2').
|
|
get_uuid_call = [mock.call('VGPU_type.get_uuid', 'type_ref_2')]
|
|
session.call_xenapi.assert_has_calls(get_uuid_call)
|
|
mock_get.assert_called_once()
|
|
self.assertEqual(expect_stats, stats)
|
|
|
|
@mock.patch.object(host.HostState, 'update_status')
|
|
@mock.patch.object(host.HostState, '_get_total_vgpu_in_grp',
|
|
return_value=7)
|
|
def test_get_vgpu_stats_in_group_multiple(self, mock_get, mock_update):
|
|
# Test when enabled multiple vGPU types in the same group.
|
|
# It should only return the first vGPU type's stats.
|
|
enabled_vgpu_types = ['type_name_1', 'type_name_2']
|
|
session = mock.Mock()
|
|
session.call_xenapi.side_effect = [
|
|
['type_ref_1', 'type_ref_2'], # GPU_group.get_enabled_VGPU_types
|
|
'type_name_1', # VGPU_type.get_model_name
|
|
'type_name_2', # VGPU_type.get_model_name
|
|
'type_uuid_1', # VGPU_type.get_uuid
|
|
'4', # VGPU_type.get_max_heads
|
|
'6', # GPU_group.get_remaining_capacity
|
|
]
|
|
host_obj = host.HostState(session)
|
|
|
|
stats = host_obj._get_vgpu_stats_in_group('grp_ref',
|
|
enabled_vgpu_types)
|
|
|
|
expect_stats = {
|
|
'uuid': 'type_uuid_1',
|
|
'type_name': 'type_name_1',
|
|
'max_heads': 4,
|
|
'total': 7,
|
|
'remaining': 6,
|
|
}
|
|
self.assertEqual(session.call_xenapi.call_count, 6)
|
|
# It should call get_uuid for the first vGPU type (the arg for get_uuid
|
|
# should be 'type_ref_1').
|
|
get_uuid_call = [mock.call('VGPU_type.get_uuid', 'type_ref_1')]
|
|
session.call_xenapi.assert_has_calls(get_uuid_call)
|
|
mock_get.assert_called_once()
|
|
self.assertEqual(expect_stats, stats)
|
|
|
|
@mock.patch.object(host.HostState, 'update_status')
|
|
@mock.patch.object(host.HostState, '_get_total_vgpu_in_grp',
|
|
return_value=7)
|
|
def test_get_vgpu_stats_in_group_cfg_not_in_grp(self, mock_get,
|
|
mock_update):
|
|
# Test when the enable_vgpu_types is not a valid
|
|
# type belong to the GPU group. It will return None.
|
|
enabled_vgpu_types = ['bad_type_name']
|
|
session = mock.Mock()
|
|
session.call_xenapi.side_effect = [
|
|
['type_ref_1', 'type_ref_2'], # GPU_group.get_enabled_VGPU_types
|
|
'type_name_1', # VGPU_type.get_model_name
|
|
'type_name_2', # VGPU_type.get_model_name
|
|
]
|
|
host_obj = host.HostState(session)
|
|
|
|
stats = host_obj._get_vgpu_stats_in_group('grp_ref',
|
|
enabled_vgpu_types)
|
|
|
|
expect_stats = None
|
|
self.assertEqual(session.call_xenapi.call_count, 3)
|
|
mock_get.assert_not_called()
|
|
self.assertEqual(expect_stats, stats)
|
|
|
|
@mock.patch.object(host.HostState, 'update_status')
|
|
def test_get_total_vgpu_in_grp(self, mock_update):
|
|
session = mock.Mock()
|
|
# The fake PGPU records returned from call_xenapi's string function:
|
|
# "PGPU.get_all_records_where".
|
|
pgpu_records = {
|
|
'pgpu_ref1': {
|
|
'enabled_VGPU_types': ['type_ref1', 'type_ref2'],
|
|
'supported_VGPU_max_capacities': {
|
|
'type_ref1': '1',
|
|
'type_ref2': '3',
|
|
}
|
|
},
|
|
'pgpu_ref2': {
|
|
'enabled_VGPU_types': ['type_ref1', 'type_ref2'],
|
|
'supported_VGPU_max_capacities': {
|
|
'type_ref1': '1',
|
|
'type_ref2': '3',
|
|
}
|
|
}
|
|
}
|
|
session.call_xenapi.return_value = pgpu_records
|
|
host_obj = host.HostState(session)
|
|
|
|
total = host_obj._get_total_vgpu_in_grp('grp_ref', 'type_ref1')
|
|
|
|
session.call_xenapi.assert_called_with(
|
|
'PGPU.get_all_records_where', 'field "GPU_group" = "grp_ref"')
|
|
# The total amount of VGPUs is equal to sum of vaiable VGPU of
|
|
# 'type_ref1' in all PGPUs.
|
|
self.assertEqual(total, 2)
|