Merge "Add functional tests and Zuul job for accelerator commands"

This commit is contained in:
Zuul authored and Gerrit Code Review committed 2026-09-23 19:24:30 +00:00
commit 37b3b0363d
7 files changed
+273

No files matched your search

+30
View File
@@ -138,6 +138,35 @@
tox_envlist: functional
tox_install_siblings: true
- job:
name: osc-functional-devstack-cyborg
parent: osc-functional-devstack
description: |
Run functional tests for OpenStackClient accelerator commands
against a devstack with Cyborg deployed using pci-sim fake
SR-IOV devices.
# pci-sim requires kernel >= 6.12; the default Ubuntu nodeset is too old.
# https://docs.openstack.org/cyborg/latest/contributor/pci-sim/kernel-dependencies.html
nodeset: devstack-single-node-debian-trixie
required-projects:
- openstack/cyborg
vars:
devstack_localrc:
# Accelerator commands are now in-tree; skip installing the
# cyborgclient plugin to avoid argparse conflicts with OSC's
# own --os-accelerator-api-version option.
CYBORG_CLIENT_INSTALL: false
ENABLE_PCI_SIM: true
PCI_SIM_NUM_PFS: 2
PCI_SIM_NUM_VFS: 4
devstack_plugins:
cyborg: https://opendev.org/openstack/cyborg
devstack_services:
cyborg-api: true
cyborg-cond: true
cyborg-agent: true
tox_extra_args: "-vv --skip-missing-interpreters=false -- openstackclient.tests.functional.accelerator"
- job:
name: osc-build-image
parent: opendev-build-docker-image
@@ -184,6 +213,7 @@
# The functional-tips job only tests the latest and shouldn't be run
# on the stable branches
branches: ^master$
- osc-functional-devstack-cyborg
gate:
jobs:
- osc-functional-devstack
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,32 @@
# 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 typing import ClassVar
from openstackclient.tests.functional import base
class AcceleratorTests(base.TestCase):
"""Functional tests for accelerator commands."""
haz_accelerator: ClassVar[bool]
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.haz_accelerator = cls.is_service_enabled('accelerator')
def setUp(self):
super().setUp()
if not self.haz_accelerator:
self.skipTest("No Accelerator service present")
@@ -0,0 +1,82 @@
# 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 json
import uuid
from openstackclient.tests.functional.accelerator.v2 import common
class TestAcceleratorRequest(common.AcceleratorTests):
"""Functional tests for accelerator ARQ commands."""
def setUp(self):
super().setUp()
self.dp_name = uuid.uuid4().hex
groups = [{'resources:CUSTOM_ACCELERATOR_FPGA': '1'}]
cmd_output = self.openstack(
'accelerator device profile create '
+ self.dp_name
+ ' '
+ "'"
+ json.dumps(groups)
+ "'",
parse_output=True,
)
self.dp_uuid = cmd_output['uuid']
self.addCleanup(
self.openstack,
'accelerator device profile delete ' + self.dp_uuid,
fail_ok=True,
)
def test_arq(self):
# create
cmd_output = self.openstack(
'accelerator arq create ' + self.dp_name, parse_output=True
)
arq_uuid = cmd_output['uuid']
self.addCleanup(
self.openstack,
'accelerator arq delete ' + arq_uuid,
fail_ok=True,
)
self.assertEqual(self.dp_name, cmd_output['device_profile_name'])
# show
cmd_output = self.openstack(
'accelerator arq show ' + arq_uuid, parse_output=True
)
self.assertEqual(arq_uuid, cmd_output['uuid'])
self.assertEqual(self.dp_name, cmd_output['device_profile_name'])
# list
cmd_output = self.openstack('accelerator arq list', parse_output=True)
uuids = [arq['uuid'] for arq in cmd_output]
self.assertIn(arq_uuid, uuids)
# list (long)
cmd_output = self.openstack(
'accelerator arq list --long', parse_output=True
)
uuids = [arq['uuid'] for arq in cmd_output]
self.assertIn(arq_uuid, uuids)
arq = next(a for a in cmd_output if a['uuid'] == arq_uuid)
self.assertIn('device_rp_uuid', arq)
self.assertIn('hostname', arq)
# delete
self.openstack('accelerator arq delete ' + arq_uuid)
cmd_output = self.openstack('accelerator arq list', parse_output=True)
uuids = [arq['uuid'] for arq in cmd_output]
self.assertNotIn(arq_uuid, uuids)
@@ -0,0 +1,47 @@
# 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 openstackclient.tests.functional.accelerator.v2 import common
class TestDevice(common.AcceleratorTests):
"""Functional tests for accelerator device commands."""
def test_device(self):
# list
cmd_output = self.openstack(
'accelerator device list', parse_output=True
)
self.assertIsInstance(cmd_output, list)
if not cmd_output:
self.skipTest("No devices available to test")
self.assertIn('uuid', cmd_output[0])
self.assertIn('type', cmd_output[0])
self.assertIn('vendor', cmd_output[0])
# list (long)
cmd_output = self.openstack(
'accelerator device list --long', parse_output=True
)
self.assertIn('model', cmd_output[0])
self.assertIn('vendor_board_info', cmd_output[0])
# show
device_uuid = cmd_output[0]['uuid']
cmd_output = self.openstack(
'accelerator device show ' + device_uuid, parse_output=True
)
self.assertEqual(device_uuid, cmd_output['uuid'])
self.assertIn('type', cmd_output)
self.assertIn('vendor', cmd_output)
self.assertIn('hostname', cmd_output)
@@ -0,0 +1,82 @@
# 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 json
import uuid
from openstackclient.tests.functional.accelerator.v2 import common
class TestDeviceProfile(common.AcceleratorTests):
"""Functional tests for accelerator device profile commands."""
def test_device_profile(self):
name = uuid.uuid4().hex
groups = [
{
'resources:CUSTOM_ACCELERATOR_FPGA': '1',
'trait:CUSTOM_FPGA_INTEL': 'required',
}
]
# create
cmd_output = self.openstack(
'accelerator device profile create '
+ name
+ ' '
+ "'"
+ json.dumps(groups)
+ "'",
parse_output=True,
)
dp_uuid = cmd_output['uuid']
self.addCleanup(
self.openstack,
'accelerator device profile delete ' + dp_uuid,
fail_ok=True,
)
self.assertEqual(name, cmd_output['name'])
self.assertEqual(groups, cmd_output['groups'])
# show
cmd_output = self.openstack(
'accelerator device profile show ' + dp_uuid,
parse_output=True,
)
self.assertEqual(name, cmd_output['name'])
self.assertEqual(groups, cmd_output['groups'])
# list
cmd_output = self.openstack(
'accelerator device profile list', parse_output=True
)
names = [dp['name'] for dp in cmd_output]
self.assertIn(name, names)
# list (long)
cmd_output = self.openstack(
'accelerator device profile list --long', parse_output=True
)
names = [dp['name'] for dp in cmd_output]
self.assertIn(name, names)
dp = next(dp for dp in cmd_output if dp['name'] == name)
self.assertIn('created_at', dp)
self.assertIn('updated_at', dp)
# delete
self.openstack('accelerator device profile delete ' + dp_uuid)
cmd_output = self.openstack(
'accelerator device profile list', parse_output=True
)
uuids = [dp['uuid'] for dp in cmd_output]
self.assertNotIn(dp_uuid, uuids)