Merge "implementation of bp disable-enable-device"

This commit is contained in:
Zuul 2023-08-25 07:30:53 +00:00 committed by Gerrit Code Review
commit ab8b851424
9 changed files with 116 additions and 5 deletions

View File

@ -13,19 +13,25 @@
# License for the specific language governing permissions and limitations
# under the License.
from http import HTTPStatus
import pecan
import wsme
from wsme import types as wtypes
from oslo_log import log
from cyborg.accelerator.common import exception
from cyborg import api
from cyborg.api.controllers import base
from cyborg.api.controllers import link
from cyborg.api.controllers import types
from cyborg.api.controllers.v2 import versions
from cyborg.api import expose
from cyborg.common import authorize_wsgi
from cyborg.common import placement_client
from cyborg import objects
LOG = log.getLogger(__name__)
@ -56,6 +62,9 @@ class Device(base.APIBase):
hostname = wtypes.text
"""The hostname of the device"""
status = wtypes.text
"""The status of the device"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link"""
@ -69,6 +78,9 @@ class Device(base.APIBase):
@classmethod
def convert_with_links(cls, obj_device):
api_device = cls(**obj_device.as_dict())
# return status filed from microversion of 2.3
if not api.request.version.minor >= versions.MINOR_3_DEVICE_STATUS:
delattr(api_device, 'status')
api_device.links = [
link.Link.make_link('self', pecan.request.public_url,
'devices', api_device.uuid)
@ -93,6 +105,8 @@ class DeviceCollection(base.APIBase):
class DevicesController(base.CyborgController):
"""REST controller for Devices."""
_custom_actions = {'disable': ['POST'], 'enable': ['POST']}
@authorize_wsgi.authorize_wsgi("cyborg:device", "get_one")
@expose.expose(Device, wtypes.text)
def get_one(self, uuid):
@ -128,3 +142,51 @@ class DevicesController(base.CyborgController):
obj_devices = objects.Device.list(context, filters=filters_dict)
LOG.info('[devices:get_all] Returned: %s', obj_devices)
return DeviceCollection.convert_with_links(obj_devices)
@authorize_wsgi.authorize_wsgi("cyborg:device", "disable")
@expose.expose(None, wtypes.text, types.uuid,
status_code=HTTPStatus.OK)
def disable(self, uuid):
context = pecan.request.context
device = objects.Device.get(context, uuid)
device.status = 'maintaining'
device.save(context)
# update resource provider inventories
client = placement_client.PlacementClient()
deployable = objects.Deployable.get_by_id(context, device.id)
filters = {'deployable_id': deployable.id, 'key': 'rc'}
attributes = objects.Attribute.get_by_filter(context, filters)
if attributes:
att_type = attributes[0].value
else:
raise exception.ResourceNotFound(
resource='Attribute',
msg='with deployable_id=%s,key=%s' % (deployable.id, 'rc'))
client.update_rp_inventory_reserved(
deployable.rp_uuid, att_type,
deployable.num_accelerators,
deployable.num_accelerators)
@authorize_wsgi.authorize_wsgi("cyborg:device", "enable")
@expose.expose(None, wtypes.text, types.uuid,
status_code=HTTPStatus.OK)
def enable(self, uuid):
context = pecan.request.context
device = objects.Device.get(context, uuid)
device.status = 'enabled'
device.save(context)
# update resource provider inventories
client = placement_client.PlacementClient()
deployable = objects.Deployable.get_by_id(context, device.id)
filters = {'deployable_id': deployable.id, 'key': 'rc'}
attributes = objects.Attribute.get_by_filter(context, filters)
if attributes:
att_type = attributes[0].value
else:
raise exception.ResourceNotFound(
resource='Attribute',
msg='with deployable_id=%s,key=%s' % (deployable.id, 'rc'))
client.update_rp_inventory_reserved(
deployable.rp_uuid, att_type,
deployable.num_accelerators,
0)

View File

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

View File

@ -37,3 +37,11 @@ Changed ``device_profile_uuid`` to ``device_profile_name_or_uuid`` in
name (newly introduced) and uuid.
- GET /v2/device_profiles/{device_profile_name_or_uuid}
2.3
---
Add new status info for list Device and get device API.
- GET: /devices
- GET: /devices/{uuid}

View File

@ -66,6 +66,10 @@ DEVICE_TYPE = (DEVICE_GPU, DEVICE_FPGA, DEVICE_AICHIP, DEVICE_QAT, DEVICE_NIC,
DEVICE_SSD)
# Device type
DEVICE_STATUS = ("enabled", "maintaining")
# Attach handle type
# 'TEST_PCI': used by fake driver, ignored by Nova virt driver.
ATTACH_HANDLE_TYPES = (AH_TYPE_PCI, AH_TYPE_MDEV, AH_TYPE_TEST_PCI) = (

View File

@ -148,7 +148,7 @@ class PlacementClient(object):
def update_inventory(
self, resource_provider_uuid, inventories,
resource_provider_generation=None):
resource_provider_generation=None, version=None):
if resource_provider_generation is None:
resource_provider_generation = self.get_resource_provider(
resource_provider_uuid=resource_provider_uuid)['generation']
@ -158,7 +158,7 @@ class PlacementClient(object):
'inventories': inventories
}
try:
return self.put(url, body).json()
return self.put(url, body, version=version).json()
except ks_exc.NotFound:
raise exception.PlacementResourceProviderNotFound(
resource_provider=resource_provider_uuid)
@ -358,3 +358,7 @@ class PlacementClient(object):
elif resp.status_code == 204:
LOG.info("Successfully delete trait %(trait_name).", {
"trait_name", name})
def update_rp_inventory_reserved(self, rp_uuid, resource, total, reserved):
update_inventory = {resource: {"total": total, "reserved": reserved}}
self.update_inventory(rp_uuid, update_inventory, version='1.26')

View File

@ -48,6 +48,12 @@ device_policies = [
policy.RuleDefault('cyborg:device:get_all',
'rule:allow',
description='Retrieve all device records'),
policy.RuleDefault('cyborg:device:disable',
'rule:admin_api',
description='Disable a device'),
policy.RuleDefault('cyborg:device:enable',
'rule:admin_api',
description='Enable a device'),
]
deployable_policies = [

View File

@ -0,0 +1,21 @@
"""add-device-status
Revision ID: 6c77bd6afea5
Revises: 4cc1d79978fc
Create Date: 2023-08-15 23:05:31.918963
"""
# revision identifiers, used by Alembic.
revision = '6c77bd6afea5'
down_revision = '4cc1d79978fc'
from alembic import op
import sqlalchemy as sa
def upgrade():
new_column = sa.Column('status', sa.Enum('enabled', 'maintaining'),
nullable=False, default='enabled')
op.add_column('devices', new_column)

View File

@ -88,6 +88,8 @@ class Device(Base):
std_board_info = Column(Text, nullable=True)
vendor_board_info = Column(Text, nullable=True)
hostname = Column(String(255), nullable=False)
status = Column(Enum("enabled", "maintaining", name='device_status'),
default='enabled', nullable=False)
class Deployable(Base):

View File

@ -29,7 +29,8 @@ LOG = logging.getLogger(__name__)
class Device(base.CyborgObject, object_base.VersionedObjectDictCompat):
# Version 1.0: Initial version
# Version 1.1: Add AICHIP, GENERIC type
VERSION = '1.1'
# Version 1.2: Add status field
VERSION = '1.2'
dbapi = dbapi.get_instance()
@ -43,6 +44,8 @@ class Device(base.CyborgObject, object_base.VersionedObjectDictCompat):
'std_board_info': object_fields.StringField(nullable=True),
'vendor_board_info': object_fields.StringField(nullable=True),
'hostname': object_fields.StringField(nullable=False),
'status': object_fields.EnumField(valid_values=constants.DEVICE_STATUS,
nullable=False, default="enabled"),
}
def create(self, context):