Merge "Adding basic implementation for Accelerator(Cyborg)"
This commit is contained in:
commit
9fc0fdaed3
@ -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",
|
||||
|
@ -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>
|
||||
|
51
doc/source/user/proxies/accelerator.rst
Normal file
51
doc/source/user/proxies/accelerator.rst
Normal 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
|
||||
|
11
doc/source/user/resources/accelerator/index.rst
Normal file
11
doc/source/user/resources/accelerator/index.rst
Normal file
@ -0,0 +1,11 @@
|
||||
Accelerator v2 Resources
|
||||
========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
v2/device
|
||||
v2/deployable
|
||||
v2/device_profile
|
||||
v2/accelerator_request
|
||||
|
@ -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:
|
13
doc/source/user/resources/accelerator/v2/deployable.rst
Normal file
13
doc/source/user/resources/accelerator/v2/deployable.rst
Normal 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:
|
||||
|
13
doc/source/user/resources/accelerator/v2/device.rst
Normal file
13
doc/source/user/resources/accelerator/v2/device.rst
Normal 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:
|
||||
|
14
doc/source/user/resources/accelerator/v2/device_profile.rst
Normal file
14
doc/source/user/resources/accelerator/v2/device_profile.rst
Normal 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:
|
||||
|
@ -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
|
||||
|
0
openstack/accelerator/__init__.py
Normal file
0
openstack/accelerator/__init__.py
Normal file
21
openstack/accelerator/accelerator_service.py
Normal file
21
openstack/accelerator/accelerator_service.py
Normal 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,
|
||||
}
|
0
openstack/accelerator/v2/__init__.py
Normal file
0
openstack/accelerator/v2/__init__.py
Normal file
173
openstack/accelerator/v2/_proxy.py
Normal file
173
openstack/accelerator/v2/_proxy.py
Normal 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)
|
98
openstack/accelerator/v2/accelerator_request.py
Normal file
98
openstack/accelerator/v2/accelerator_request.py
Normal 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)
|
66
openstack/accelerator/v2/deployable.py
Normal file
66
openstack/accelerator/v2/deployable.py
Normal 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
|
44
openstack/accelerator/v2/device.py
Normal file
44
openstack/accelerator/v2/device.py
Normal 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')
|
48
openstack/accelerator/v2/device_profile.py
Normal file
48
openstack/accelerator/v2/device_profile.py
Normal 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)
|
27
openstack/accelerator/version.py
Normal file
27
openstack/accelerator/version.py
Normal 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')
|
154
openstack/cloud/_accelerator.py
Normal file
154
openstack/cloud/_accelerator.py
Normal 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)
|
@ -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)
|
||||
|
0
openstack/tests/unit/accelerator/__init__.py
Normal file
0
openstack/tests/unit/accelerator/__init__.py
Normal file
42
openstack/tests/unit/accelerator/test_version.py
Normal file
42
openstack/tests/unit/accelerator/test_version.py
Normal 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)
|
0
openstack/tests/unit/accelerator/v2/__init__.py
Normal file
0
openstack/tests/unit/accelerator/v2/__init__.py
Normal 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)
|
52
openstack/tests/unit/accelerator/v2/test_deployable.py
Normal file
52
openstack/tests/unit/accelerator/v2/test_deployable.py
Normal 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)
|
53
openstack/tests/unit/accelerator/v2/test_device.py
Normal file
53
openstack/tests/unit/accelerator/v2/test_device.py
Normal 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)
|
54
openstack/tests/unit/accelerator/v2/test_device_profile.py
Normal file
54
openstack/tests/unit/accelerator/v2/test_device_profile.py
Normal 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)
|
66
openstack/tests/unit/accelerator/v2/test_proxy.py
Normal file
66
openstack/tests/unit/accelerator/v2/test_proxy.py
Normal 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)
|
@ -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.
|
||||
|
||||
|
328
openstack/tests/unit/cloud/test_accelerator.py
Normal file
328
openstack/tests/unit/cloud/test_accelerator.py
Normal 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()
|
27
openstack/tests/unit/fixtures/accelerator.json
Normal file
27
openstack/tests/unit/fixtures/accelerator.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Add support for Cyborg(accelerator)
|
Loading…
Reference in New Issue
Block a user