c3245098e3
function test - boot/soft reboot/hard reboot - delete - rebuild - un/pause - stop/start - lock/unlock - rescue/unrescue - reject: resize/suspend/migrate/shelve/evacuate Implements: blueprint sriov-smartnic-support Co-Authored-By: Xinran Wang <xin-ran.wang@intel.com> Change-Id: I1d25a3a00380cac07547f53e75259ac0711c949c
334 lines
12 KiB
Python
334 lines
12 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 copy
|
|
|
|
import fixtures
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
|
|
from nova import exception
|
|
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def _get_device_profile(dp_name, trait):
|
|
dp = {
|
|
'fakedev-dp': [
|
|
{
|
|
'name': 'fakedev-dp',
|
|
'uuid': 'cbec22f3-ac29-444e-b4bb-98509f32faae',
|
|
'groups': [
|
|
{
|
|
'resources:FPGA': 1,
|
|
'trait:' + trait: 'required',
|
|
},
|
|
],
|
|
# Skipping links key in Cyborg API return value
|
|
},
|
|
],
|
|
'fakedev-dp-port': [
|
|
{
|
|
'name': 'fakedev-dp',
|
|
'uuid': 'cbec22f3-ac29-444e-b4bb-98509f32faae',
|
|
'groups': [
|
|
{
|
|
'resources:FPGA': 1,
|
|
'trait:' + trait: 'required',
|
|
},
|
|
],
|
|
# Skipping links key in Cyborg API return value
|
|
},
|
|
],
|
|
'fakedev-dp-multi': [
|
|
{
|
|
'name': 'fakedev-dp-multi',
|
|
'uuid': 'cbec22f3-ac29-444e-b4bb-98509f32faae',
|
|
'groups': [
|
|
{
|
|
'resources:FPGA': 2,
|
|
'resources:FPGA2': 1,
|
|
'trait:' + trait: 'required',
|
|
},
|
|
],
|
|
# Skipping links key in Cyborg API return value
|
|
},
|
|
],
|
|
}
|
|
return dp[dp_name]
|
|
|
|
|
|
def get_arqs(dp_name):
|
|
# prepare fixture arqs and bound info
|
|
arqs = [
|
|
{
|
|
'uuid': 'b59d34d3-787b-4fb0-a6b9-019cd81172f8',
|
|
'device_profile_name': dp_name,
|
|
'device_profile_group_id': 0,
|
|
'state': 'Initial',
|
|
'device_rp_uuid': None,
|
|
'hostname': None,
|
|
'instance_uuid': None,
|
|
'attach_handle_info': {},
|
|
'attach_handle_type': '',
|
|
},
|
|
{'uuid': '73d5f9f3-23e9-4b45-909a-e8a1db4cf24c',
|
|
'device_profile_name': dp_name,
|
|
'device_profile_group_id': 0,
|
|
'state': 'Initial',
|
|
'device_rp_uuid': None,
|
|
'hostname': None,
|
|
'instance_uuid': None,
|
|
'attach_handle_info': {},
|
|
'attach_handle_type': '',
|
|
},
|
|
{'uuid': '69b83caf-dd1c-493d-8796-40af5a16e3f6',
|
|
'device_profile_name': dp_name,
|
|
'device_profile_group_id': 0,
|
|
'state': 'Initial',
|
|
'device_rp_uuid': None,
|
|
'hostname': None,
|
|
'instance_uuid': None,
|
|
'attach_handle_info': {},
|
|
'attach_handle_type': '',
|
|
},
|
|
{'uuid': 'e5fc1da7-216b-4102-a50d-43ba77bcacf7',
|
|
'device_profile_name': dp_name,
|
|
'device_profile_group_id': 0,
|
|
'state': 'Initial',
|
|
'device_rp_uuid': None,
|
|
'hostname': None,
|
|
'instance_uuid': None,
|
|
'attach_handle_info': {},
|
|
'attach_handle_type': '',
|
|
}
|
|
]
|
|
# arqs bound info
|
|
attach_handle_list = [
|
|
{
|
|
'bus': '0c',
|
|
'device': '0',
|
|
'domain': '0000',
|
|
'function': '1',
|
|
'physical_network': 'PHYNET1'
|
|
},
|
|
{
|
|
'bus': '0c',
|
|
'device': '0',
|
|
'domain': '0000',
|
|
'function': '2',
|
|
'physical_network': 'PHYNET1'
|
|
},
|
|
{
|
|
'bus': '0c',
|
|
'device': '0',
|
|
'domain': '0000',
|
|
'function': '3',
|
|
'physical_network': 'PHYNET1'
|
|
},
|
|
{
|
|
'bus': '0c',
|
|
'device': '0',
|
|
'domain': '0000',
|
|
'function': '4',
|
|
'physical_network': 'PHYNET1'
|
|
}
|
|
]
|
|
|
|
bound_arqs = []
|
|
# combine bond info to arq generating a bonded arqs list
|
|
for idx, arq in enumerate(arqs):
|
|
bound_arq = copy.deepcopy(arq)
|
|
bound_arq.update(
|
|
{'state': 'Bound',
|
|
'attach_handle_type': 'TEST_PCI',
|
|
'attach_handle_info': attach_handle_list[idx]},
|
|
)
|
|
bound_arqs.append(bound_arq)
|
|
return arqs, bound_arqs
|
|
|
|
|
|
class CyborgFixture(fixtures.Fixture):
|
|
"""Fixture that mocks Cyborg APIs used by nova/accelerator/cyborg.py"""
|
|
|
|
dp_name = 'fakedev-dp'
|
|
trait = 'CUSTOM_FAKE_DEVICE'
|
|
arq_list, bound_arq_list = copy.deepcopy(get_arqs(dp_name))
|
|
arq_uuids = []
|
|
for arq in arq_list:
|
|
arq_uuids.append(arq["uuid"])
|
|
call_create_arq_count = 0
|
|
|
|
# NOTE(Sundar): The bindings passed to the fake_bind_arqs() from the
|
|
# conductor are indexed by ARQ UUID and include the host name, device
|
|
# RP UUID and instance UUID. (See params to fake_bind_arqs below.)
|
|
#
|
|
# Later, when the compute manager calls fake_get_arqs_for_instance() with
|
|
# the instance UUID, the returned ARQs must contain the host name and
|
|
# device RP UUID. But these can vary from test to test.
|
|
#
|
|
# So, fake_bind_arqs() below takes bindings indexed by ARQ UUID and
|
|
# converts them to bindings indexed by instance UUID, which are then
|
|
# stored in the dict below. This dict looks like:
|
|
# { $instance_uuid: [
|
|
# {'hostname': $hostname,
|
|
# 'device_rp_uuid': $device_rp_uuid,
|
|
# 'arq_uuid': $arq_uuid
|
|
# }
|
|
# ]
|
|
# }
|
|
# Since it is indexed by instance UUID, and that is presumably unique
|
|
# across concurrently executing tests, this should be safe for
|
|
# concurrent access.
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.mock_get_dp = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient._get_device_profile_list',
|
|
side_effect=self.fake_get_device_profile_list)).mock
|
|
self.mock_create_arqs = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.create_arqs',
|
|
side_effect=self.fake_create_arqs)).mock
|
|
self.mock_bind_arqs = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.bind_arqs',
|
|
side_effect=self.fake_bind_arqs)).mock
|
|
self.mock_get_arqs = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.'
|
|
'get_arqs_for_instance',
|
|
side_effect=self.fake_get_arqs_for_instance)).mock
|
|
self.mock_del_arqs = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.'
|
|
'delete_arqs_for_instance',
|
|
side_effect=self.fake_delete_arqs_for_instance)).mock
|
|
self.mock_get_arq_by_uuid = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.'
|
|
'get_arq_by_uuid',
|
|
side_effect=self.fake_get_arq_by_uuid)).mock
|
|
self.mock_get_arq_device_rp_uuid = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.'
|
|
'get_arq_device_rp_uuid',
|
|
side_effect=self.fake_get_arq_device_rp_uuid)).mock
|
|
self.mock_create_arq_and_match_rp = self.useFixture(fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.'
|
|
'create_arqs_and_match_resource_providers',
|
|
side_effect=self.fake_create_arq_and_match_rp)).mock
|
|
self.mock_fake_delete_arqs_by_uuid = self.useFixture(
|
|
fixtures.MockPatch(
|
|
'nova.accelerator.cyborg._CyborgClient.'
|
|
'delete_arqs_by_uuid',
|
|
side_effect=self.fake_delete_arqs_by_uuid)).mock
|
|
|
|
def fake_get_device_profile_list(self, dp_name):
|
|
return _get_device_profile(dp_name, self.trait)
|
|
|
|
@staticmethod
|
|
def fake_bind_arqs(bindings):
|
|
"""Simulate Cyborg ARQ bindings.
|
|
|
|
Since Nova calls Cyborg for binding on per-instance basis, the
|
|
instance UUIDs would be the same for all ARQs in a single call.
|
|
|
|
This function converts bindings indexed by ARQ UUID to bindings
|
|
indexed by instance UUID, so that fake_get_arqs_for_instance can
|
|
retrieve them later.
|
|
|
|
:param bindings:
|
|
{ "$arq_uuid": {
|
|
"hostname": STRING
|
|
"device_rp_uuid": UUID
|
|
"instance_uuid": UUID
|
|
},
|
|
...
|
|
}
|
|
:returns: None
|
|
"""
|
|
if bindings.keys() and CyborgFixture.arq_uuids is None:
|
|
LOG.error("ARQ not found")
|
|
raise exception.AcceleratorRequestOpFailed()
|
|
for arq_uuid, binding in bindings.items():
|
|
for bound_arq in CyborgFixture.bound_arq_list:
|
|
if arq_uuid == bound_arq["uuid"]:
|
|
bound_arq["hostname"] = binding["hostname"]
|
|
bound_arq["instance_uuid"] = binding["instance_uuid"]
|
|
bound_arq["device_rp_uuid"] = binding["device_rp_uuid"]
|
|
break
|
|
|
|
@staticmethod
|
|
def fake_get_arqs_for_instance(instance_uuid, only_resolved=False):
|
|
"""Get list of bound ARQs for this instance.
|
|
|
|
This function uses bindings indexed by instance UUID to
|
|
populate the bound ARQ templates in CyborgFixture.bound_arq_list.
|
|
"""
|
|
bound_arq_list = copy.deepcopy(CyborgFixture.bound_arq_list)
|
|
instance_bound_arqs = []
|
|
for arq in bound_arq_list:
|
|
if arq["instance_uuid"] == instance_uuid:
|
|
instance_bound_arqs.append(arq)
|
|
return instance_bound_arqs
|
|
|
|
def fake_get_arq_by_uuid(self, uuid):
|
|
for arq in self.arq_list:
|
|
if uuid == arq['uuid']:
|
|
return arq
|
|
return None
|
|
|
|
def fake_delete_arqs_for_instance(self, instance_uuid):
|
|
# clean up arq binding info while delete arqs
|
|
for arq in self.bound_arq_list:
|
|
if arq["instance_uuid"] == instance_uuid:
|
|
arq["instance_uuid"] = None
|
|
arq["hostname"] = None
|
|
arq["device_rp_uuid"] = None
|
|
|
|
def fake_create_arq_and_match_rp(self,
|
|
dp_name, rg_rp_map=None, owner=None):
|
|
# sync the device_rp_uuid to fake arq
|
|
arqs = self.fake_create_arqs(dp_name)
|
|
for arq in arqs:
|
|
dp_group_id = arq['device_profile_group_id']
|
|
requester_id = ("device_profile_" + str(dp_group_id) +
|
|
(str(owner) if owner else ""))
|
|
arq["device_rp_uuid"] = rg_rp_map[requester_id][0]
|
|
return arqs
|
|
|
|
def fake_create_arqs(self, dp_name):
|
|
index = self.call_create_arq_count
|
|
self.call_create_arq_count += 1
|
|
if index < len(self.arq_list):
|
|
return [self.arq_list[index]]
|
|
else:
|
|
return None
|
|
|
|
def fake_get_arq_device_rp_uuid(self,
|
|
arq_arg, rg_rp_map=None, port_id=None):
|
|
# sync the device_rp_uuid to fake arq
|
|
for arq in self.arq_list:
|
|
if arq["uuid"] == arq_arg['uuid']:
|
|
dp_group_id = arq['device_profile_group_id']
|
|
requester_id = ("device_profile_" +
|
|
str(dp_group_id) + str(port_id))
|
|
arq["device_rp_uuid"] = rg_rp_map[requester_id][0]
|
|
return arq["device_rp_uuid"]
|
|
return None
|
|
|
|
def fake_delete_arqs_by_uuid(self, arq_uuids):
|
|
# clean up arq binding info while delete arqs
|
|
for arq_uuid in arq_uuids:
|
|
for arq in self.bound_arq_list:
|
|
if arq["uuid"] == arq_uuid:
|
|
arq["instance_uuid"] = None
|
|
arq["hostname"] = None
|
|
arq["device_rp_uuid"] = None
|