Merge "Adding basic implementation for Accelerator(Cyborg)"

This commit is contained in:
Zuul 2020-02-26 17:23:45 +00:00 committed by Gerrit Code Review
commit 9fc0fdaed3
32 changed files with 1454 additions and 2 deletions

View File

@ -33,7 +33,8 @@ class EnforcementError(errors.SphinxError):
def get_proxy_methods():
"""Return a set of public names on all proxies"""
names = ["openstack.baremetal.v1._proxy",
names = ["openstack.accelerator.v2._proxy",
"openstack.baremetal.v1._proxy",
"openstack.clustering.v1._proxy",
"openstack.block_storage.v2._proxy",
"openstack.compute.v2._proxy",

View File

@ -95,6 +95,7 @@ control which services can be used.
.. toctree::
:maxdepth: 1
Accelerator <proxies/accelerator>
Baremetal <proxies/baremetal>
Baremetal Introspection <proxies/baremetal_introspection>
Block Storage <proxies/block_storage>
@ -129,6 +130,7 @@ The following services have exposed *Resource* classes.
.. toctree::
:maxdepth: 1
Accelerator <resources/accelerator/index>
Baremetal <resources/baremetal/index>
Baremetal Introspection <resources/baremetal_introspection/index>
Block Storage <resources/block_storage/index>

View File

@ -0,0 +1,51 @@
Accelerator API
===============
.. automodule:: openstack.accelerator.v2._proxy
The Accelerator Class
---------------------
The accelerator high-level interface is available through the ``accelerator``
member of a :class:`~openstack.connection.Connection` object.
The ``accelerator`` member will only be added if the service is detected.
Device Operations
^^^^^^^^^^^^^^^^^
.. autoclass:: openstack.accelerator.v2._proxy.Proxy
.. automethod:: openstack.accelerator.v2._proxy.Proxy.get_device
.. automethod:: openstack.accelerator.v2._proxy.Proxy.devices
Deployable Operations
^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openstack.accelerator.v2._proxy.Proxy
.. automethod:: openstack.accelerator.v2._proxy.Proxy.update_deployable
.. automethod:: openstack.accelerator.v2._proxy.Proxy.get_deployable
.. automethod:: openstack.accelerator.v2._proxy.Proxy.deployables
Device Profile Operations
^^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openstack.accelerator.v2._proxy.Proxy
.. automethod:: openstack.accelerator.v2._proxy.Proxy.create_device_profile
.. automethod:: openstack.accelerator.v2._proxy.Proxy.delete_device_profile
.. automethod:: openstack.accelerator.v2._proxy.Proxy.get_device_profile
.. automethod:: openstack.accelerator.v2._proxy.Proxy.device_profiles
Accelerator Request Operations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openstack.accelerator.v2._proxy.Proxy
.. automethod:: openstack.accelerator.v2._proxy.Proxy.create_accelerator_request
.. automethod:: openstack.accelerator.v2._proxy.Proxy.delete_accelerator_request
.. automethod:: openstack.accelerator.v2._proxy.Proxy.get_accelerator_request
.. automethod:: openstack.accelerator.v2._proxy.Proxy.accelerator_requests
.. automethod:: openstack.accelerator.v2._proxy.Proxy.update_accelerator_request

View File

@ -0,0 +1,11 @@
Accelerator v2 Resources
========================
.. toctree::
:maxdepth: 1
v2/device
v2/deployable
v2/device_profile
v2/accelerator_request

View File

@ -0,0 +1,13 @@
openstack.accelerator.v2.accelerator_request
============================================
.. automodule:: openstack.accelerator.v2.accelerator_request
The AcceleratorRequest Class
----------------------------
The ``AcceleratorRequest`` class inherits from
:class:`~openstack.resource.Resource`.
.. autoclass:: openstack.accelerator.v2.accelerator_request.AcceleratorRequest
:members:

View File

@ -0,0 +1,13 @@
openstack.accelerator.v2.deployable
============================================
.. automodule:: openstack.accelerator.v2.deployable
The Deployable Class
--------------------
The ``Deployable`` class inherits from :class:`~openstack.resource.Resource`.
.. autoclass:: openstack.accelerator.v2.deployable.Deployable
:members:

View File

@ -0,0 +1,13 @@
openstack.accelerator.v2.device
============================================
.. automodule:: openstack.accelerator.v2.device
The Device Class
--------------------
The ``Device`` class inherits from :class:`~openstack.resource.Resource`.
.. autoclass:: openstack.accelerator.v2.device.Device
:members:

View File

@ -0,0 +1,14 @@
openstack.accelerator.v2.device_profile
============================================
.. automodule:: openstack.accelerator.v2.device_profile
The DeviceProfile Class
-----------------------
The ``DeviceProfile`` class inherits from
:class:`~openstack.resource.Resource`.
.. autoclass:: openstack.accelerator.v2.device_profile.DeviceProfile
:members:

View File

@ -1,5 +1,6 @@
# Generated file, to change, run tools/print-services.py
from openstack import service_description
from openstack.accelerator import accelerator_service
from openstack.baremetal import baremetal_service
from openstack.baremetal_introspection import baremetal_introspection_service
from openstack.block_storage import block_storage_service
@ -124,7 +125,7 @@ class ServicesMixin(object):
function_engine = service_description.ServiceDescription(service_type='function-engine')
accelerator = service_description.ServiceDescription(service_type='accelerator')
accelerator = accelerator_service.AcceleratorService(service_type='accelerator')
admin_logic = service_description.ServiceDescription(service_type='admin-logic')
registration = admin_logic

View File

View File

@ -0,0 +1,21 @@
# 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 openstack import service_description
from openstack.accelerator.v2 import _proxy as _proxy_v2
class AcceleratorService(service_description.ServiceDescription):
"""The accelerator service."""
supported_versions = {
'2': _proxy_v2.Proxy,
}

View File

View File

@ -0,0 +1,173 @@
# 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 openstack import proxy
from openstack.accelerator.v2 import deployable as _deployable
from openstack.accelerator.v2 import device as _device
from openstack.accelerator.v2 import device_profile as _device_profile
from openstack.accelerator.v2 import accelerator_request as _arq
class Proxy(proxy.Proxy):
def deployables(self, **query):
"""Retrieve a generator of deployables.
:param kwargs query: Optional query parameters to be sent to
restrict the deployables to be returned.
:returns: A generator of deployable instances.
"""
return self._list(_deployable.Deployable, **query)
def get_deployable(self, uuid, fields=None):
"""Get a single deployable.
:param uuid: The value can be the UUID of a deployable.
:returns: One :class:`~openstack.accelerator.v2.deployable.Deployable`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
deployable matching the criteria could be found.
"""
return self._get(_deployable.Deployable, uuid)
def update_deployable(self, uuid, patch):
"""Reconfig the FPGA with new bitstream.
:param uuid: The value can be the UUID of a deployable
:param patch: The infomation of to reconfig.
:returns: The results of FPGA reconfig.
"""
return self._get_resource(_deployable.Deployable,
uuid).patch(self, patch)
def devices(self, **query):
"""Retrieve a generator of devices.
:param kwargs query: Optional query parameters to be sent to
restrict the devices to be returned. Available parameters include:
* hostname: The hostname of the device.
* type: The type of the device.
* vendor: The vendor ID of the device.
* sort: A list of sorting keys separated by commas. Each sorting
key can optionally be attached with a sorting direction
modifier which can be ``asc`` or ``desc``.
* limit: Requests a specified size of returned items from the
query. Returns a number of items up to the specified limit
value.
* marker: Specifies the ID of the last-seen item. Use the limit
parameter to make an initial limited request and use the ID of
the last-seen item from the response as the marker parameter
value in a subsequent limited request.
:returns: A generator of device instances.
"""
return self._list(_device.Device, **query)
def get_device(self, uuid, fields=None):
"""Get a single device.
:param uuid: The value can be the UUID of a device.
:returns: One :class:`~openstack.accelerator.v2.device.Device`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
deployable matching the criteria could be found.
"""
return self._get(_device.Device, uuid)
def device_profiles(self, **query):
"""Retrieve a generator of device profiles.
:param kwargs query: Optional query parameters to be sent to
restrict the device profiles to be returned.
:returns: A generator of device profile instances.
"""
return self._list(_device_profile.DeviceProfile, **query)
def create_device_profile(self, **attrs):
"""Create a device_profiles.
:param kwargs attrs: a list of device_profiles.
:returns: The list of created device profiles
"""
return self._create(_device_profile.DeviceProfile, **attrs)
def delete_device_profile(self, name_or_id, ignore_missing=True):
"""Delete an device profile
:param name_or_id: The value can be either the ID of
an device profile.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be
raised when the device profile does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent device profile.
:returns: ``None``
"""
return self._delete(_device_profile.DeviceProfile,
name_or_id, ignore_missing=ignore_missing)
def get_device_profile(self, uuid, fields=None):
"""Get a single device profile.
:param uuid: The value can be the UUID of a device profile.
:returns: One :class:
`~openstack.accelerator.v2.device_profile.DeviceProfile`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
device profile matching the criteria could be found.
"""
return self._get(_device_profile.DeviceProfile, uuid)
def accelerator_requests(self, **query):
"""Retrieve a generator of accelerator requests.
:param kwargs query: Optional query parameters to be sent to
restrict the accelerator requests to be returned.
:returns: A generator of accelerator request instances.
"""
return self._list(_arq.AcceleratorRequest, **query)
def create_accelerator_request(self, **attrs):
"""Create an ARQs for a single device profile.
:param kwargs attrs: request body.
"""
return self._create(_arq.AcceleratorRequest, **attrs)
def delete_accelerator_request(self, name_or_id, ignore_missing=True):
"""Delete an device profile
:param name_or_id: The value can be either the ID of
an accelerator request.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be
raised when the device profile does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent accelerator request.
:returns: ``None``
"""
return self._delete(_arq.AcceleratorRequest, name_or_id,
ignore_missing=ignore_missing)
def get_accelerator_request(self, uuid, fields=None):
"""Get a single accelerator request.
:param uuid: The value can be the UUID of a accelerator request.
:returns: One :class:
`~openstack.accelerator.v2.accelerator_request.AcceleratorRequest`
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
accelerator request matching the criteria could be found.
"""
return self._get(_arq.AcceleratorRequest, uuid)
def update_accelerator_request(self, uuid, properties):
"""Bind/Unbind an accelerator to VM.
:param uuid: The uuid of the accelerator_request to be binded/unbinded.
:param properties: The info of VM
that will bind/unbind the accelerator.
:returns: True if bind/unbind succeeded, False otherwise.
"""
return self._get_resource(_arq.AcceleratorRequest,
uuid).patch(self, properties)

View File

@ -0,0 +1,98 @@
# 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 openstack import resource
from openstack import exceptions
class AcceleratorRequest(resource.Resource):
resource_key = 'arq'
resources_key = 'arqs'
base_path = '/accelerator_requests'
# capabilities
allow_create = True
allow_fetch = True
allow_delete = True
allow_list = True
#: Allow patch operation for binding.
allow_patch = True
#: The device address associated with this ARQ (if any)
attach_handle_info = resource.Body('attach_handle_info')
#: The type of attach handle (e.g. PCI, mdev...)
attach_handle_type = resource.Body('attach_handle_type')
#: The name of the device profile
device_profile_name = resource.Body('device_profile_name')
#: The id of the device profile group
device_profile_group_id = resource.Body('device_profile_group_id')
#: The UUID of the bound device RP (if any)
device_rp_uuid = resource.Body('device_rp_uuid')
#: The host name to which ARQ is bound. (if any)
hostname = resource.Body('hostname')
#: The UUID of the instance associated with this ARQ (if any)
instance_uuid = resource.Body('instance_uuid')
#: The state of the ARQ
state = resource.Body('state')
#: The UUID of the ARQ
uuid = resource.Body('uuid', alternate_id=True)
def _convert_patch(self, patch):
# This overrides the default behavior of _convert_patch because
# the PATCH method consumes JSON, its key is the ARQ uuid
# and its value is an ordinary JSON patch. spec:
# https://specs.openstack.org/openstack/cyborg-specs/specs/train/approved/cyborg-api
converted = super(AcceleratorRequest, self)._convert_patch(patch)
converted = {self.id: converted}
return converted
def patch(self, session, patch=None, prepend_key=True, has_body=True,
retry_on_conflict=None, base_path=None):
# This overrides the default behavior of patch because
# the PATCH method consumes a dict rather than a list. spec:
# https://specs.openstack.org/openstack/cyborg-specs/specs/train/approved/cyborg-api
# The id cannot be dirty for an commit
self._body._dirty.discard("id")
# Only try to update if we actually have anything to commit.
if not patch and not self.requires_commit:
return self
if not self.allow_patch:
raise exceptions.MethodNotSupported(self, "patch")
request = self._prepare_request(prepend_key=prepend_key,
base_path=base_path, patch=True)
microversion = self._get_microversion_for(session, 'patch')
if patch:
request.body = self._convert_patch(patch)
return self._commit(session, request, 'PATCH', microversion,
has_body=has_body,
retry_on_conflict=retry_on_conflict)
def _consume_attrs(self, mapping, attrs):
# This overrides the default behavior of _consume_attrs because
# cyborg api returns an ARQ as list. spec:
# https://specs.openstack.org/openstack/cyborg-specs/specs/train/approved/cyborg-api
if isinstance(self, AcceleratorRequest):
if self.resources_key in attrs:
attrs = attrs[self.resources_key][0]
return super(AcceleratorRequest, self)._consume_attrs(mapping, attrs)
def create(self, session, base_path=None):
# This overrides the default behavior of resource creation because
# cyborg doesn't accept resource_key in its request.
return super(AcceleratorRequest, self).create(
session, prepend_key=False, base_path=base_path)

View File

@ -0,0 +1,66 @@
# 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 openstack import exceptions
from openstack import resource
class Deployable(resource.Resource):
resource_key = 'deployable'
resources_key = 'deployables'
base_path = '/deployables'
# capabilities
allow_create = False
allow_fetch = True
allow_commit = False
allow_delete = False
allow_list = True
allow_patch = True
#: The timestamp when this deployable was created.
created_at = resource.Body('created_at')
#: The device_id of the deployable.
device_id = resource.Body('device_id')
#: The UUID of the deployable.
id = resource.Body('uuid', alternate_id=True)
#: The name of the deployable.
name = resource.Body('name')
#: The num_accelerator of the deployable.
num_accelerators = resource.Body('num_accelerators')
#: The parent_id of the deployable.
parent_id = resource.Body('parent_id')
#: The root_id of the deployable.
root_id = resource.Body('root_id')
#: The timestamp when this deployable was updated.
updated_at = resource.Body('updated_at')
def _commit(self, session, request, method, microversion, has_body=True,
retry_on_conflict=None):
session = self._get_session(session)
kwargs = {}
retriable_status_codes = set(session.retriable_status_codes or ())
if retry_on_conflict:
kwargs['retriable_status_codes'] = retriable_status_codes | {409}
elif retry_on_conflict is not None and retriable_status_codes:
# The baremetal proxy defaults to retrying on conflict, allow
# overriding it via an explicit retry_on_conflict=False.
kwargs['retriable_status_codes'] = retriable_status_codes - {409}
try:
call = getattr(session, method.lower())
except AttributeError:
raise exceptions.ResourceFailure(
msg="Invalid commit method: %s" % method)
request.url = request.url + "/program"
response = call(request.url, json=request.body,
headers=request.headers, microversion=microversion,
**kwargs)
self.microversion = microversion
self._translate_response(response, has_body=has_body)
return self

View File

@ -0,0 +1,44 @@
# 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 openstack import resource
class Device(resource.Resource):
resource_key = 'device'
resources_key = 'devices'
base_path = '/devices'
# capabilities
allow_create = False
allow_fetch = True
allow_commit = False
allow_delete = False
allow_list = True
#: The timestamp when this device was created.
created_at = resource.Body('created_at')
#: The hostname of the device.
hostname = resource.Body('hostname')
#: The ID of the device.
id = resource.Body('id')
#: The model of the device.
model = resource.Body('model')
#: The std board information of the device.
std_board_info = resource.Body('std_board_info')
#: The type of the device.
type = resource.Body('type')
#: The timestamp when this device was updated.
updated_at = resource.Body('updated_at')
#: The UUID of the device.
uuid = resource.Body('uuid', alternate_id=True)
#: The vendor ID of the device.
vendor = resource.Body('vendor')
#: The vendor board information of the device.
vendor_board_info = resource.Body('vendor_board_info')

View File

@ -0,0 +1,48 @@
# 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 openstack import resource
class DeviceProfile(resource.Resource):
resource_key = 'device_profile'
resources_key = 'device_profiles'
base_path = '/device_profiles'
# capabilities
allow_create = True
allow_fetch = True
allow_commit = False
allow_delete = True
allow_list = True
#: The timestamp when this device_profile was created.
created_at = resource.Body('created_at')
#: The groups of the device profile
groups = resource.Body('groups')
#: The name of the device profile
name = resource.Body('name')
#: The timestamp when this device_profile was updated.
updated_at = resource.Body('updated_at')
#: The uuid of the device profile
uuid = resource.Body('uuid', alternate_id=True)
# TODO(s_shogo): This implementation only treat [ DeviceProfile ], and
# cannot treat multiple DeviceProfiles in list.
def _prepare_request_body(self, patch, prepend_key):
body = super(DeviceProfile, self)._prepare_request_body(
patch, prepend_key)
return [body]
def create(self, session, base_path=None):
# This overrides the default behavior of resource creation because
# cyborg doesn't accept resource_key in its request.
return super(DeviceProfile, self).create(
session, prepend_key=False, base_path=base_path)

View File

@ -0,0 +1,27 @@
# 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 openstack import resource
class Version(resource.Resource):
resource_key = 'version'
resources_key = 'versions'
base_path = '/'
# capabilities
allow_list = True
# Properties
links = resource.Body('links')
status = resource.Body('status')

View File

@ -0,0 +1,154 @@
# 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 types so that we can reference ListType in sphinx param declarations.
# We can't just use list, because sphinx gets confused by
# openstack.resource.Resource.list and openstack.resource2.Resource.list
from openstack.cloud import _normalize
class AcceleratorCloudMixin(_normalize.Normalizer):
def list_deployables(self, filters=None):
"""List all available deployables.
:param filters: (optional) dict of filter conditions to push down
:returns: A list of deployable info.
"""
# Translate None from search interface to empty {} for kwargs below
if not filters:
filters = {}
return list(self.accelerator.deployables(**filters))
def list_devices(self, filters=None):
"""List all devices.
:param filters: (optional) dict of filter conditions to push down
:returns: A list of device info.
"""
# Translate None from search interface to empty {} for kwargs below
if not filters:
filters = {}
return list(self.accelerator.devices(**filters))
def list_device_profiles(self, filters=None):
"""List all device_profiles.
:param filters: (optional) dict of filter conditions to push down
:returns: A list of device profile info.
"""
# Translate None from search interface to empty {} for kwargs below
if not filters:
filters = {}
return list(self.accelerator.device_profiles(**filters))
def create_device_profile(self, attrs):
"""Create a device_profile.
:param attrs: The info of device_profile to be created.
:returns: A ``munch.Munch`` of the created device_profile.
"""
return self.accelerator.create_device_profile(**attrs)
def delete_device_profile(self, name_or_id, filters):
"""Delete a device_profile.
:param name_or_id: The Name(or uuid) of device_profile to be deleted.
:returns: True if delete succeeded, False otherwise.
"""
device_profile = self.accelerator.get_device_profile(
name_or_id,
filters
)
if device_profile is None:
self.log.debug(
"device_profile %s not found for deleting",
name_or_id
)
return False
self.accelerator.delete_device_profile(name_or_id=name_or_id)
return True
def list_accelerator_requests(self, filters=None):
"""List all accelerator_requests.
:param filters: (optional) dict of filter conditions to push down
:returns: A list of accelerator request info.
"""
# Translate None from search interface to empty {} for kwargs below
if not filters:
filters = {}
return list(self.accelerator.accelerator_requests(**filters))
def delete_accelerator_request(self, name_or_id, filters):
"""Delete a accelerator_request.
:param name_or_id: The Name(or uuid) of accelerator_request.
:returns: True if delete succeeded, False otherwise.
"""
accelerator_request = self.accelerator.get_accelerator_request(
name_or_id,
filters
)
if accelerator_request is None:
self.log.debug(
"accelerator_request %s not found for deleting",
name_or_id
)
return False
self.accelerator.delete_accelerator_request(name_or_id=name_or_id)
return True
def create_accelerator_request(self, attrs):
"""Create an accelerator_request.
:param attrs: The info of accelerator_request to be created.
:returns: A ``munch.Munch`` of the created accelerator_request.
"""
return self.accelerator.create_accelerator_request(**attrs)
def bind_accelerator_request(self, uuid, properties):
"""Bind an accelerator to VM.
:param uuid: The uuid of the accelerator_request to be binded.
:param properties: The info of VM that will bind the accelerator.
:returns: True if bind succeeded, False otherwise.
"""
accelerator_request = self.accelerator.get_accelerator_request(uuid)
if accelerator_request is None:
self.log.debug(
"accelerator_request %s not found for unbinding", uuid
)
return False
return self.accelerator.update_accelerator_request(uuid, properties)
def unbind_accelerator_request(self, uuid, properties):
"""Unbind an accelerator from VM.
:param uuid: The uuid of the accelerator_request to be unbinded.
:param properties: The info of VM that will unbind the accelerator.
:returns:True if unbind succeeded, False otherwise.
"""
accelerator_request = self.accelerator.get_accelerator_request(uuid)
if accelerator_request is None:
self.log.debug(
"accelerator_request %s not found for unbinding", uuid
)
return False
return self.accelerator.update_accelerator_request(uuid, properties)

View File

@ -186,6 +186,7 @@ import six
from openstack import _log
from openstack import _services_mixin
from openstack.cloud import openstackcloud as _cloud
from openstack.cloud import _accelerator
from openstack.cloud import _baremetal
from openstack.cloud import _block_storage
from openstack.cloud import _compute
@ -249,6 +250,7 @@ def from_config(cloud=None, config=None, options=None, **kwargs):
class Connection(
_services_mixin.ServicesMixin,
_cloud._OpenStackCloudMixin,
_accelerator.AcceleratorCloudMixin,
_baremetal.BaremetalCloudMixin,
_block_storage.BlockStorageCloudMixin,
_compute.ComputeCloudMixin,
@ -385,6 +387,7 @@ class Connection(
# Call the _*CloudMixin constructors while we work on
# integrating things better.
_cloud._OpenStackCloudMixin.__init__(self)
_accelerator.AcceleratorCloudMixin.__init__(self)
_baremetal.BaremetalCloudMixin.__init__(self)
_block_storage.BlockStorageCloudMixin.__init__(self)
_clustering.ClusteringCloudMixin.__init__(self)

View File

@ -0,0 +1,42 @@
# 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 openstack.tests.unit import base
from openstack.accelerator import version
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'id': IDENTIFIER,
'links': '2',
'status': '3',
}
class TestVersion(base.TestCase):
def test_basic(self):
sot = version.Version()
self.assertEqual('version', sot.resource_key)
self.assertEqual('versions', sot.resources_key)
self.assertEqual('/', sot.base_path)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_fetch)
self.assertFalse(sot.allow_commit)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = version.Version(**EXAMPLE)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['links'], sot.links)
self.assertEqual(EXAMPLE['status'], sot.status)

View File

@ -0,0 +1,59 @@
# 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 openstack.tests.unit import base
from openstack.accelerator.v2 import accelerator_request as arq
FAKE_ID = '0725b527-e51a-41df-ad22-adad5f4546ad'
FAKE_RP_UUID = 'f4b7fe6c-8ab4-4914-a113-547af022935b'
FAKE_INSTANCE_UUID = '1ce4a597-9836-4e02-bea1-a3a6cbe7b9f9'
FAKE_ATTACH_INFO_STR = '{"bus": "5e", '\
'"device": "00", '\
'"domain": "0000", '\
'"function": "1"}'
FAKE = {
'uuid': FAKE_ID,
'device_profile_name': 'fake-devprof',
'device_profile_group_id': 0,
'device_rp_uuid': FAKE_RP_UUID,
'instance_uuid': FAKE_INSTANCE_UUID,
'attach_handle_type': 'PCI',
'attach_handle_info': FAKE_ATTACH_INFO_STR,
}
class TestAcceleratorRequest(base.TestCase):
def test_basic(self):
sot = arq.AcceleratorRequest()
self.assertEqual('arq', sot.resource_key)
self.assertEqual('arqs', sot.resources_key)
self.assertEqual('/accelerator_requests', sot.base_path)
self.assertTrue(sot.allow_create)
self.assertTrue(sot.allow_fetch)
self.assertFalse(sot.allow_commit)
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
self.assertTrue(sot.allow_patch)
def test_make_it(self):
sot = arq.AcceleratorRequest(**FAKE)
self.assertEqual(FAKE_ID, sot.uuid)
self.assertEqual(FAKE['device_profile_name'], sot.device_profile_name)
self.assertEqual(FAKE['device_profile_group_id'],
sot.device_profile_group_id)
self.assertEqual(FAKE_RP_UUID, sot.device_rp_uuid)
self.assertEqual(FAKE_INSTANCE_UUID, sot.instance_uuid)
self.assertEqual(FAKE['attach_handle_type'], sot.attach_handle_type)
self.assertEqual(FAKE_ATTACH_INFO_STR, sot.attach_handle_info)

View File

@ -0,0 +1,52 @@
# 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 uuid
from openstack.tests.unit import base
from openstack.accelerator.v2 import deployable
EXAMPLE = {
'uuid': uuid.uuid4(),
'created_at': '2019-08-09T12:14:57.233772',
'updated_at': '2019-08-09T12:15:57.233772',
'parent_id': '1',
'root_id': '1',
'name': 'test_name',
'num_accelerators': '1',
'device_id': '1',
}
class TestDeployable(base.TestCase):
def test_basic(self):
sot = deployable.Deployable()
self.assertEqual('deployable', sot.resource_key)
self.assertEqual('deployables', sot.resources_key)
self.assertEqual('/deployables', sot.base_path)
self.assertFalse(sot.allow_create)
self.assertTrue(sot.allow_fetch)
self.assertFalse(sot.allow_commit)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = deployable.Deployable(**EXAMPLE)
self.assertEqual(EXAMPLE['uuid'], sot.id)
self.assertEqual(EXAMPLE['parent_id'], sot.parent_id)
self.assertEqual(EXAMPLE['root_id'], sot.root_id)
self.assertEqual(EXAMPLE['name'], sot.name)
self.assertEqual(EXAMPLE['num_accelerators'], sot.num_accelerators)
self.assertEqual(EXAMPLE['device_id'], sot.device_id)
self.assertEqual(EXAMPLE['created_at'], sot.created_at)
self.assertEqual(EXAMPLE['updated_at'], sot.updated_at)

View File

@ -0,0 +1,53 @@
# 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 uuid
from openstack.tests.unit import base
from openstack.accelerator.v2 import device
EXAMPLE = {
'id': '1',
'uuid': uuid.uuid4(),
'created_at': '2019-08-09T12:14:57.233772',
'updated_at': '2019-08-09T12:15:57.233772',
'type': 'test_type',
'vendor': '0x8086',
'model': 'test_model',
'std_board_info': '{"product_id": "0x09c4"}',
'vendor_board_info': 'test_vb_info',
}
class TestDevice(base.TestCase):
def test_basic(self):
sot = device.Device()
self.assertEqual('device', sot.resource_key)
self.assertEqual('devices', sot.resources_key)
self.assertEqual('/devices', sot.base_path)
self.assertFalse(sot.allow_create)
self.assertTrue(sot.allow_fetch)
self.assertFalse(sot.allow_commit)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = device.Device(**EXAMPLE)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['uuid'], sot.uuid)
self.assertEqual(EXAMPLE['type'], sot.type)
self.assertEqual(EXAMPLE['vendor'], sot.vendor)
self.assertEqual(EXAMPLE['model'], sot.model)
self.assertEqual(EXAMPLE['std_board_info'], sot.std_board_info)
self.assertEqual(EXAMPLE['vendor_board_info'], sot.vendor_board_info)
self.assertEqual(EXAMPLE['created_at'], sot.created_at)
self.assertEqual(EXAMPLE['updated_at'], sot.updated_at)

View File

@ -0,0 +1,54 @@
# 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 openstack.tests.unit import base
from openstack.accelerator.v2 import device_profile
FAKE = {
"id": 1,
"uuid": u"a95e10ae-b3e3-4eab-a513-1afae6f17c51",
"name": u'afaas_example_1',
"groups": [
{"resources:ACCELERATOR_FPGA": "1",
"trait:CUSTOM_FPGA_INTEL_PAC_ARRIA10": "required",
"trait:CUSTOM_FUNCTION_ID_3AFB": "required",
},
{"resources:CUSTOM_ACCELERATOR_FOO": "2",
"resources:CUSTOM_MEMORY": "200",
"trait:CUSTOM_TRAIT_ALWAYS": "required",
}
]
}
class TestDeviceProfile(base.TestCase):
def test_basic(self):
sot = device_profile.DeviceProfile()
self.assertEqual('device_profile', sot.resource_key)
self.assertEqual('device_profiles', sot.resources_key)
self.assertEqual('/device_profiles', sot.base_path)
self.assertTrue(sot.allow_create)
self.assertTrue(sot.allow_fetch)
self.assertFalse(sot.allow_commit)
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
self.assertFalse(sot.allow_patch)
def test_make_it(self):
sot = device_profile.DeviceProfile(**FAKE)
self.assertEqual(FAKE['id'], sot.id)
self.assertEqual(FAKE['uuid'], sot.uuid)
self.assertEqual(FAKE['name'], sot.name)
self.assertEqual(FAKE['groups'], sot.groups)

View File

@ -0,0 +1,66 @@
# 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 openstack.accelerator.v2 import _proxy
from openstack.accelerator.v2 import deployable
from openstack.accelerator.v2 import device_profile
from openstack.accelerator.v2 import accelerator_request
from openstack.tests.unit import test_proxy_base as test_proxy_base
class TestAcceleratorProxy(test_proxy_base.TestProxyBase):
def setUp(self):
super(TestAcceleratorProxy, self).setUp()
self.proxy = _proxy.Proxy(self.session)
def test_list_deployables(self):
self.verify_list(self.proxy.deployables, deployable.Deployable)
def test_list_device_profile(self):
self.verify_list(self.proxy.device_profiles,
device_profile.DeviceProfile)
def test_create_device_profile(self):
self.verify_create(self.proxy.create_device_profile,
device_profile.DeviceProfile)
def test_delete_device_profile(self):
self.verify_delete(self.proxy.delete_device_profile,
device_profile.DeviceProfile, False)
def test_delete_device_profile_ignore(self):
self.verify_delete(self.proxy.delete_device_profile,
device_profile.DeviceProfile, True)
def test_get_device_profile(self):
self.verify_get(self.proxy.get_device_profile,
device_profile.DeviceProfile)
def test_list_accelerator_request(self):
self.verify_list(self.proxy.accelerator_requests,
accelerator_request.AcceleratorRequest)
def test_create_accelerator_request(self):
self.verify_create(self.proxy.create_accelerator_request,
accelerator_request.AcceleratorRequest)
def test_delete_accelerator_request(self):
self.verify_delete(self.proxy.delete_accelerator_request,
accelerator_request.AcceleratorRequest, False)
def test_delete_accelerator_request_ignore(self):
self.verify_delete(self.proxy.delete_accelerator_request,
accelerator_request.AcceleratorRequest, True)
def test_get_accelerator_request(self):
self.verify_get(self.proxy.get_accelerator_request,
accelerator_request.AcceleratorRequest)

View File

@ -564,6 +564,12 @@ class TestCase(base.TestCase):
compute_version_json, compute_discovery_url),
])
def get_cyborg_discovery_mock_dict(self):
discovery_fixture = os.path.join(
self.fixtures_directory, "accelerator.json")
return dict(method='GET', uri="https://accelerator.example.com/",
text=open(discovery_fixture, 'r').read())
def use_glance(
self, image_version_json='image-version.json',
image_discovery_url='https://image.example.com/'):
@ -611,6 +617,15 @@ class TestCase(base.TestCase):
self.__do_register_uris([
self.get_senlin_discovery_mock_dict()])
def use_cyborg(self):
# NOTE(s_shogo): This method is only meant to be used in "setUp"
# where the ordering of the url being registered is tightly controlled
# if the functionality of .use_cyborg is meant to be used during an
# actual test case, use .get_cyborg_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris
self.__do_register_uris([
self.get_cyborg_discovery_mock_dict()])
def register_uris(self, uri_mock_list=None):
"""Mock a list of URIs and responses via requests mock.

View File

@ -0,0 +1,328 @@
# 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 openstack.tests.unit import base
import copy
import uuid
DEP_UUID = uuid.uuid4().hex
DEP_DICT = {
'uuid': DEP_UUID,
'name': 'dep_name',
'parent_id': None,
'root_id': 1,
'num_accelerators': 4,
'device_id': 0
}
DEV_UUID = uuid.uuid4().hex
DEV_DICT = {
'id': 1,
'uuid': DEV_UUID,
'name': 'dev_name',
'type': 'test_type',
'vendor': '0x8086',
'model': 'test_model',
'std_board_info': '{"product_id": "0x09c4"}',
'vendor_board_info': 'test_vb_info',
}
DEV_PROF_UUID = uuid.uuid4().hex
DEV_PROF_GROUPS = [
{"resources:ACCELERATOR_FPGA": "1",
"trait:CUSTOM_FPGA_INTEL_PAC_ARRIA10": "required",
"trait:CUSTOM_FUNCTION_ID_3AFB": "required",
},
{"resources:CUSTOM_ACCELERATOR_FOO": "2",
"resources:CUSTOM_MEMORY": "200",
"trait:CUSTOM_TRAIT_ALWAYS": "required",
}
]
DEV_PROF_DICT = {
"id": 1,
"uuid": DEV_PROF_UUID,
"name": 'afaas_example_1',
"groups": DEV_PROF_GROUPS,
}
NEW_DEV_PROF_DICT = copy.copy(DEV_PROF_DICT)
ARQ_UUID = uuid.uuid4().hex
ARQ_DEV_RP_UUID = uuid.uuid4().hex
ARQ_INSTANCE_UUID = uuid.uuid4().hex
ARQ_ATTACH_INFO_STR = '{"bus": "5e", '\
'"device": "00", '\
'"domain": "0000", '\
'"function": "1"}'
ARQ_DICT = {
'uuid': ARQ_UUID,
'hostname': 'test_hostname',
'device_profile_name': 'fake-devprof',
'device_profile_group_id': 0,
'device_rp_uuid': ARQ_DEV_RP_UUID,
'instance_uuid': ARQ_INSTANCE_UUID,
'attach_handle_type': 'PCI',
'attach_handle_info': ARQ_ATTACH_INFO_STR,
}
NEW_ARQ_DICT = copy.copy(ARQ_DICT)
class TestAccelerator(base.TestCase):
def setUp(self):
super(TestAccelerator, self).setUp()
self.use_cyborg()
def test_list_deployables(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'deployables']),
json={'deployables': [DEP_DICT]}
),
])
dep_list = self.cloud.list_deployables()
self.assertEqual(len(dep_list), 1)
self.assertEqual(dep_list[0].id, DEP_DICT['uuid'])
self.assertEqual(dep_list[0].name, DEP_DICT['name'])
self.assertEqual(dep_list[0].parent_id, DEP_DICT['parent_id'])
self.assertEqual(dep_list[0].root_id, DEP_DICT['root_id'])
self.assertEqual(dep_list[0].num_accelerators,
DEP_DICT['num_accelerators'])
self.assertEqual(dep_list[0].device_id, DEP_DICT['device_id'])
self.assert_calls()
def test_list_devices(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'devices']),
json={'devices': [DEV_DICT]}
),
])
dev_list = self.cloud.list_devices()
self.assertEqual(len(dev_list), 1)
self.assertEqual(dev_list[0].id, DEV_DICT['id'])
self.assertEqual(dev_list[0].uuid, DEV_DICT['uuid'])
self.assertEqual(dev_list[0].name, DEV_DICT['name'])
self.assertEqual(dev_list[0].type, DEV_DICT['type'])
self.assertEqual(dev_list[0].vendor, DEV_DICT['vendor'])
self.assertEqual(dev_list[0].model, DEV_DICT['model'])
self.assertEqual(dev_list[0].std_board_info,
DEV_DICT['std_board_info'])
self.assertEqual(dev_list[0].vendor_board_info,
DEV_DICT['vendor_board_info'])
self.assert_calls()
def test_list_device_profiles(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'device_profiles']),
json={'device_profiles': [DEV_PROF_DICT]}
),
])
dev_prof_list = self.cloud.list_device_profiles()
self.assertEqual(len(dev_prof_list), 1)
self.assertEqual(dev_prof_list[0].id, DEV_PROF_DICT['id'])
self.assertEqual(dev_prof_list[0].uuid, DEV_PROF_DICT['uuid'])
self.assertEqual(dev_prof_list[0].name, DEV_PROF_DICT['name'])
self.assertEqual(dev_prof_list[0].groups, DEV_PROF_DICT['groups'])
self.assert_calls()
def test_create_device_profile(self):
self.register_uris([
dict(method='POST',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'device_profiles']),
json=NEW_DEV_PROF_DICT)
])
attrs = {
'name': NEW_DEV_PROF_DICT['name'],
'groups': NEW_DEV_PROF_DICT['groups']
}
self.assertTrue(
self.cloud.create_device_profile(
attrs
)
)
self.assert_calls()
def test_delete_device_profile(self, filters=None):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'device_profiles', DEV_PROF_DICT['name']]),
json={"device_profiles": [DEV_PROF_DICT]}),
dict(method='DELETE',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'device_profiles', DEV_PROF_DICT['name']]),
json=DEV_PROF_DICT)
])
self.assertTrue(
self.cloud.delete_device_profile(
DEV_PROF_DICT['name'],
filters
)
)
self.assert_calls()
def test_list_accelerator_requests(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests']),
json={'arqs': [ARQ_DICT]}
),
])
arq_list = self.cloud.list_accelerator_requests()
self.assertEqual(len(arq_list), 1)
self.assertEqual(arq_list[0].uuid, ARQ_DICT['uuid'])
self.assertEqual(arq_list[0].device_profile_name,
ARQ_DICT['device_profile_name'])
self.assertEqual(arq_list[0].device_profile_group_id,
ARQ_DICT['device_profile_group_id'])
self.assertEqual(arq_list[0].device_rp_uuid,
ARQ_DICT['device_rp_uuid'])
self.assertEqual(arq_list[0].instance_uuid,
ARQ_DICT['instance_uuid'])
self.assertEqual(arq_list[0].attach_handle_type,
ARQ_DICT['attach_handle_type'])
self.assertEqual(arq_list[0].attach_handle_info,
ARQ_DICT['attach_handle_info'])
self.assert_calls()
def test_create_accelerator_request(self):
self.register_uris([
dict(method='POST',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests']),
json=NEW_ARQ_DICT
),
])
attrs = {
'device_profile_name': NEW_ARQ_DICT['device_profile_name'],
'device_profile_group_id': NEW_ARQ_DICT['device_profile_group_id']
}
self.assertTrue(
self.cloud.create_accelerator_request(
attrs
)
)
self.assert_calls()
def test_delete_accelerator_request(self, filters=None):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests', ARQ_DICT['uuid']]),
json={"accelerator_requests": [ARQ_DICT]}),
dict(method='DELETE',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests', ARQ_DICT['uuid']]),
json=ARQ_DICT)
])
self.assertTrue(
self.cloud.delete_accelerator_request(
ARQ_DICT['uuid'],
filters
)
)
self.assert_calls()
def test_bind_accelerator_request(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests', ARQ_DICT['uuid']]),
json={"accelerator_requests": [ARQ_DICT]}),
dict(method='PATCH',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests', ARQ_DICT['uuid']]),
json=ARQ_DICT)
])
properties = [{'path': '/hostname',
'value': ARQ_DICT['hostname'],
'op': 'add'},
{'path': '/instance_uuid',
'value': ARQ_DICT['instance_uuid'],
'op': 'add'},
{'path': '/device_rp_uuid',
'value': ARQ_DICT['device_rp_uuid'],
'op': 'add'}]
self.assertTrue(
self.cloud.bind_accelerator_request(
ARQ_DICT['uuid'], properties
)
)
self.assert_calls()
def test_unbind_accelerator_request(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests', ARQ_DICT['uuid']]),
json={"accelerator_requests": [ARQ_DICT]}),
dict(method='PATCH',
uri=self.get_mock_url(
'accelerator',
'public',
append=['v2', 'accelerator_requests', ARQ_DICT['uuid']]),
json=ARQ_DICT)
])
properties = [{'path': '/hostname',
'op': 'remove'},
{'path': '/instance_uuid',
'op': 'remove'},
{'path': '/device_rp_uuid',
'op': 'remove'}]
self.assertTrue(
self.cloud.unbind_accelerator_request(
ARQ_DICT['uuid'], properties
)
)
self.assert_calls()

View File

@ -0,0 +1,27 @@
{
"versions": [
{
"id": "2.0",
"links": [
{
"href": "/v2/",
"rel": "self"
},
{
"href": "https://accelerator.example.com/api-ref/accelerator",
"rel": "help"
}
],
"max_version": "2.0",
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.accelerator-v1+json"
}
],
"min_version": "2.0",
"status": "CURRENT",
"updated": "2019-09-01T00:00:00Z"
}
]
}

View File

@ -0,0 +1,3 @@
---
features:
- Add support for Cyborg(accelerator)