Get device profile by name

Nowadays showing device_profile is supported only by uuid,
this patch supports to getting device profile by name.

Implements: blueprint show-device-profile-with-name
Change-Id: Ib9aff3312676923fc7478f2bcef79333e10496bf
This commit is contained in:
ericxiett 2021-11-11 00:54:33 +00:00
parent 2490e5b78f
commit cde7b78e22
9 changed files with 276 additions and 177 deletions

View File

@ -1,156 +1,157 @@
.. -*- rst -*- .. -*- rst -*-
.. needs:body_verification .. needs:body_verification
Device Profiles Device Profiles
+++++++++++++++ +++++++++++++++
Lists, creates, shows details for, updates and deletes device profiles. Lists, creates, shows details for, updates and deletes device profiles.
A `device_profile A `device_profile
<http://specs.openstack.org/openstack/cyborg-specs/specs/train/implemented/device-profiles.html>`_ <http://specs.openstack.org/openstack/cyborg-specs/specs/train/implemented/device-profiles.html>`_
is a named set of the user requirements for one or more is a named set of the user requirements for one or more
accelerators. It can be viewed as a flavor for devices. Broadly it includes accelerators. It can be viewed as a flavor for devices. Broadly it includes
two things: the desired amounts of specific resource classes and the two things: the desired amounts of specific resource classes and the
requirements that the resource provider(s) must satisfy. While the resource requirements that the resource provider(s) must satisfy. While the resource
classes are the same as those known to Placement, some requirements would classes are the same as those known to Placement, some requirements would
correspond to Placement traits and others to properties that Cyborg alone correspond to Placement traits and others to properties that Cyborg alone
knows about. knows about.
List Device Profiles List Device Profiles
-------------------- --------------------
.. rest_method:: GET /v2/device_profiles .. rest_method:: GET /v2/device_profiles
Lists UUIDs, names, groups for all device profiles. Lists UUIDs, names, groups for all device profiles.
Normal response codes: 200 Normal response codes: 200
Error response codes: unauthorized(401), forbidden(403) Error response codes: unauthorized(401), forbidden(403)
Response Response
======== ========
.. rest_parameters:: parameters.yaml .. rest_parameters:: parameters.yaml
- device_profiles: device_profiles - device_profiles: device_profiles
- name: device_prof_name_resp - name: device_prof_name_resp
- uuid: device_profile_uuid - uuid: device_profile_uuid
- description: device_profile_description - description: device_profile_description
- groups: device_prof_groups_resp - groups: device_prof_groups_resp
- created_at: created - created_at: created
- updated_at: updated - updated_at: updated
- links: links - links: links
**Example response: list all device profiles** **Example response: list all device profiles**
.. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-list-resp.json .. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-list-resp.json
:language: javascript :language: javascript
Get One Device Profile Get One Device Profile
---------------------- ----------------------
.. rest_method:: GET /v2/device_profiles/{device_profile_uuid} .. rest_method:: GET /v2/device_profiles/{device_profile_name_or_uuid}
Gets the UUID, name, groups for one device_profile with the specified UUID. Gets the UUID, name, groups for one device_profile with the specified name or UUID.
Normal response codes: 200 Normal response codes: 200
Error response codes: badRequest(400), unauthorized(401), forbidden(403) Error response codes: badRequest(400), unauthorized(401), forbidden(403)
Request Request
======= =======
.. rest_parameters:: parameters.yaml .. rest_parameters:: parameters.yaml
- device_profile_uuid: device_profile_uuid - device_profile_uuid: device_profile_uuid_v_21
- device_profile_name_or_uuid: device_profile_name_or_uuid
Response
======== Response
.. rest_parameters:: parameters.yaml ========
.. rest_parameters:: parameters.yaml
- device_profile: device_profile
- name: device_prof_name_resp - device_profile: device_profile
- uuid: device_profile_uuid - name: device_prof_name_resp
- description: device_profile_description - uuid: device_profile_uuid
- groups: device_prof_groups_resp - description: device_profile_description
- created_at: created - groups: device_prof_groups_resp
- updated_at: updated - created_at: created
- links: links - updated_at: updated
- links: links
**Example response: get details of a specific device profile**
**Example response: get details of a specific device profile(v2.2)**
.. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-getone-resp.json
:language: javascript .. literalinclude:: ../../doc/api_samples/device_profiles/v22/device_profiles-getone-resp.json
:language: javascript
Create Device Profile
--------------------- Create Device Profile
---------------------
.. rest_method:: POST /v2/device_profiles
.. rest_method:: POST /v2/device_profiles
Creates a device profile. The payload should have these fields:
Creates a device profile. The payload should have these fields:
Normal response codes: 201
Normal response codes: 201
Error response codes: badRequest(400), unauthorized(401), forbidden(403),
conflict(409) Error response codes: badRequest(400), unauthorized(401), forbidden(403),
conflict(409)
Request
======= Request
.. rest_parameters:: parameters.yaml =======
.. rest_parameters:: parameters.yaml
- name: device_prof_name_req
- groups: device_prof_groups_req - name: device_prof_name_req
- groups: device_prof_groups_req
Response
======== Response
.. rest_parameters:: parameters.yaml ========
.. rest_parameters:: parameters.yaml
- name: device_prof_name_resp
- uuid: device_profile_uuid - name: device_prof_name_resp
- description: device_profile_description - uuid: device_profile_uuid
- groups: device_prof_groups_resp - description: device_profile_description
- created_at: created - groups: device_prof_groups_resp
- updated_at: updated - created_at: created
- links: links - updated_at: updated
- links: links
**Example post curl with resource/trait**
**Example post curl with resource/trait**
.. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-post-curl.json
:language: javascript .. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-post-curl.json
:language: javascript
**Example post curl with a cyborg property when bitstream is required**
**Example post curl with a cyborg property when bitstream is required**
.. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-post-curl-with-bitstream.json
:language: javascript .. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-post-curl-with-bitstream.json
:language: javascript
**Example response: create a device profile**
**Example response: create a device profile**
.. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-create-resp.json
:language: javascript .. literalinclude:: ../../doc/api_samples/device_profiles/device_profiles-create-resp.json
:language: javascript
Delete One Device Profile by uuid
--------------------------------- Delete One Device Profile by uuid
---------------------------------
.. rest_method:: DELETE /v2/device_profiles/{device_profile_uuid}
.. rest_method:: DELETE /v2/device_profiles/{device_profile_uuid}
Delete a device profile. No query parameters required.
Delete a device profile. No query parameters required.
Delete Multiple Device Profiles by names
---------------------------------------- Delete Multiple Device Profiles by names
----------------------------------------
.. rest_method:: DELETE /v2/device_profiles?value={device_profile_name1},{device_profile_name2}
.. rest_method:: DELETE /v2/device_profiles?value={device_profile_name1},{device_profile_name2}
In the URL, Device profiles to be deleted should be in comma-delimited list of
device profile names. In the URL, Device profiles to be deleted should be in comma-delimited list of
device profile names.
.. note::
Today we do not allow deletion of a device profile when it is in use by .. note::
VMs, because ARQs have a foreign key on device profile table. But we copy Today we do not allow deletion of a device profile when it is in use by
the device profile groups into the ARQ, so this foreign key is not needed. VMs, because ARQs have a foreign key on device profile table. But we copy
So we can improve in Ussuri. the device profile groups into the ARQ, so this foreign key is not needed.
So we can improve in Ussuri.
Response
======== Response
========
Normal response codes: 204
Normal response codes: 204
There is no body content for the response of a successful DELETE query
There is no body content for the response of a successful DELETE query

View File

@ -12,13 +12,28 @@ deployable_uuid:
in: path in: path
required: true required: true
type: string type: string
device_profile_uuid: device_profile_name_or_uuid:
description: | description: |
The UUID of the device_profile for your accelerator request. The name or uuid of the device_profile for your accelerator request.
This must be a valid UUID otherwise API will return 400.
in: path in: path
required: true required: true
type: string type: string
min_version: 2.2
device_profile_uuid:
description: |
The uuid of the device_profile for your accelerator request.
This must be a valid uuid otherwise api will return 400.
in: path
required: true
type: string
device_profile_uuid_v_21:
description: |
The uuid of the device_profile for your accelerator request.
This must be a valid uuid otherwise api will return 400.
in: path
required: true
type: string
max_version: 2.1
device_uuid: device_uuid:
description: | description: |
The UUID of the device. The UUID of the device.

View File

@ -23,13 +23,16 @@ from wsme import types as wtypes
from oslo_log import log from oslo_log import log
from oslo_utils import uuidutils from oslo_utils import uuidutils
from cyborg import api
from cyborg.api.controllers import base from cyborg.api.controllers import base
from cyborg.api.controllers import link from cyborg.api.controllers import link
from cyborg.api.controllers import types from cyborg.api.controllers import types
from cyborg.api.controllers.v2 import versions
from cyborg.api import expose from cyborg.api import expose
from cyborg.common import authorize_wsgi from cyborg.common import authorize_wsgi
from cyborg.common import constants from cyborg.common import constants
from cyborg.common import exception from cyborg.common import exception
from cyborg.common.i18n import _
from cyborg import objects from cyborg import objects
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -257,21 +260,33 @@ class DeviceProfilesController(base.CyborgController,
@authorize_wsgi.authorize_wsgi("cyborg:device_profile", "get_one") @authorize_wsgi.authorize_wsgi("cyborg:device_profile", "get_one")
@expose.expose('json', wtypes.text) @expose.expose('json', wtypes.text)
def get_one(self, uuid): def get_one(self, dp_uuid_or_name):
"""Retrieve a single device profile by uuid.""" """Retrieve a single device profile by uuid or name."""
LOG.info('[device_profiles] get_one. uuid=%s', uuid) context = pecan.request.context
obj_devprofs = self._get_device_profile_list(uuid=uuid) if uuidutils.is_uuid_like(dp_uuid_or_name):
api_obj_devprofs = self.get_device_profiles(obj_devprofs) LOG.info('[device_profiles] get_one. uuid=%s', dp_uuid_or_name)
if len(api_obj_devprofs) == 0: obj_devprof = objects.DeviceProfile.get_by_uuid(context,
dp_uuid_or_name)
else:
if api.request.version.minor >= versions.MINOR_2_DP_BY_NAME:
LOG.info('[device_profiles] get_one. name=%s', dp_uuid_or_name)
obj_devprof = \
objects.DeviceProfile.get_by_name(context,
dp_uuid_or_name)
else:
raise exception.NotAcceptable(_(
"Request not acceptable. The minimal required API "
"version should be %(base)s.%(opr)s") %
{'base': versions.BASE_VERSION,
'opr': versions.MINOR_2_DP_BY_NAME})
if not obj_devprof:
LOG.warning("Device profile with %s not found!", dp_uuid_or_name)
raise exception.ResourceNotFound( raise exception.ResourceNotFound(
resource='Device profile', resource='Device profile',
msg='with uuid %s' % uuid) msg='with %s' % dp_uuid_or_name)
api_obj_devprof = self.get_device_profile(obj_devprof)
count = len(api_obj_devprofs) ret = {"device_profile": api_obj_devprof}
if count != 1: # Should never happen because names are unique
raise exception.ExpectedOneObject(obj='device profile',
count=count)
ret = {"device_profile": api_obj_devprofs[0]}
LOG.info('[device_profiles] get_one returned: %s', ret) LOG.info('[device_profiles] get_one returned: %s', ret)
# TODO(Sundar) Replace this with convert_with_links() # TODO(Sundar) Replace this with convert_with_links()
return wsme.api.Response(ret, status_code=HTTPStatus.OK, return wsme.api.Response(ret, status_code=HTTPStatus.OK,

View File

@ -24,8 +24,10 @@ BASE_VERSION = 2
# #
# v2.0: Initial minor version. # v2.0: Initial minor version.
# v2.1: Add project_id for arq patch # v2.1: Add project_id for arq patch
# v2.2: Support getting device profile by name (newly introduced) and uuid.
MINOR_0_INITIAL_VERSION = 0 MINOR_0_INITIAL_VERSION = 0
MINOR_1_PROJECT_ID = 1 MINOR_1_PROJECT_ID = 1
MINOR_2_DP_BY_NAME = 2
# When adding another version, update: # When adding another version, update:
@ -34,7 +36,7 @@ MINOR_1_PROJECT_ID = 1
# explanation of what changed in the new version # explanation of what changed in the new version
MINOR_MAX_VERSION = MINOR_1_PROJECT_ID MINOR_MAX_VERSION = MINOR_2_DP_BY_NAME
# String representations of the minor and maximum versions # String representations of the minor and maximum versions
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_0_INITIAL_VERSION) _MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_0_INITIAL_VERSION)

View File

@ -27,3 +27,11 @@ microversions.
Add ``project_id`` for Accelerator Requests PATCH API. ``project_id`` is Add ``project_id`` for Accelerator Requests PATCH API. ``project_id`` is
used to control the operation of arq with different roles. used to control the operation of arq with different roles.
2.2
---
Changed ``device_profile_uuid`` to ``device_profile_name_or_uuid`` in
`Get One Device Profile` API path, so support getting device profile by
name (newly introduced) and uuid.
- GET /v2/device_profiles/{device_profile_name_or_uuid}

View File

@ -19,6 +19,7 @@ import webtest
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from cyborg.api.controllers import base
from cyborg.tests.unit.api.controllers.v2 import base as v2_test from cyborg.tests.unit.api.controllers.v2 import base as v2_test
from cyborg.tests.unit import fake_device_profile from cyborg.tests.unit import fake_device_profile
@ -51,13 +52,40 @@ class TestDeviceProfileController(v2_test.APITestV2):
# Check that the link is properly set up # Check that the link is properly set up
self._validate_links(out_dp['links'], in_dp['uuid']) self._validate_links(out_dp['links'], in_dp['uuid'])
@mock.patch('cyborg.objects.DeviceProfile.list') @mock.patch('cyborg.objects.DeviceProfile.get_by_uuid')
def test_get_one_by_uuid(self, mock_dp): def test_get_one_by_uuid(self, mock_dp_uuid):
dp = self.fake_dp_objs[0] dp = self.fake_dp_objs[0]
mock_dp.return_value = [dp] mock_dp_uuid.return_value = dp
url = self.DP_URL + '/%s' url = self.DP_URL + '/%s'
data = self.get_json(url % dp['uuid'], headers=self.headers) data = self.get_json(url % dp['uuid'], headers=self.headers)
mock_dp.assert_called_once() mock_dp_uuid.assert_called_once()
out_dp = data['device_profile']
self._validate_dp(dp, out_dp)
@mock.patch('cyborg.objects.DeviceProfile.get_by_name')
def test_get_one_by_name_before_v22(self, mock_dp_name):
dp = self.fake_dp_objs[0]
mock_dp_name.return_value = dp
url = self.DP_URL + '/%s'
headers = self.headers
headers[base.Version.current_api_version] = 'accelerator 2.1'
self.assertRaisesRegex(
webtest.app.AppError,
"Request not acceptable.*",
self.get_json,
url % dp['name'],
headers=headers)
@mock.patch('cyborg.objects.DeviceProfile.get_by_name')
def test_get_one_by_name(self, mock_dp_name):
dp = self.fake_dp_objs[0]
mock_dp_name.return_value = dp
url = self.DP_URL + '/%s'
headers = self.headers
headers[base.Version.current_api_version] = 'accelerator 2.2'
data = self.get_json(url % dp['name'],
headers=headers)
mock_dp_name.assert_called_once()
out_dp = data['device_profile'] out_dp = data['device_profile']
self._validate_dp(dp, out_dp) self._validate_dp(dp, out_dp)

View File

@ -5,7 +5,7 @@
"description": "", "description": "",
"groups":[ "groups":[
{ {
"trait:CUSTOM_CHENKE_TRAITS":"required", "trait:CUSTOM_FPGA_INSPUR":"required",
"resources:FPGA":"1", "resources:FPGA":"1",
"accel:bitstream_id":"d5ca2f11-3108-4426-a11c-a959987565df" "accel:bitstream_id":"d5ca2f11-3108-4426-a11c-a959987565df"
} }

View File

@ -0,0 +1,22 @@
{
"device_profile":{
"name":"fpga-dp1",
"uuid":"5518a925-1c2c-49a2-a8bf-0927d9456f3e",
"description": "",
"groups":[
{
"trait:CUSTOM_FPGA_INSPUR":"required",
"resources:FPGA":"1",
"accel:bitstream_id":"d5ca2f11-3108-4426-a11c-a959987565df"
}
],
"created_at": "2020-03-09 11:26:05+00:00",
"updated_at": null,
"links":[
{
"href":"http://192.168.32.217/accelerator/v2/device_profiles/5518a925-1c2c-49a2-a8bf-0927d9456f3e",
"rel":"self"
}
]
}
}

View File

@ -0,0 +1,8 @@
---
features:
- |
Changed ``device_profile_uuid`` to ``device_profile_name_or_uuid`` in
`Get One Device Profile` API path, so it can support getting one device
profile by its `name` since microversion 2.2, not just uuid.
- GET /v2/device_profiles/{device_profile_name_or_uuid}