Implement driver vendor passthrough
* Define 'list' and 'call' vendor_passthru methods in Driver * Add corresponding proxy methods * Implement unit tests - verify result properly returned for list_vendor_passthru - verify session is being called properly for both list and call Story: 2008193 Task: 40960 Change-Id: Id7e5f6d3f651c371efca29002de63c973e8f33d7
This commit is contained in:
@@ -194,6 +194,35 @@ class Proxy(proxy.Proxy):
|
||||
"""
|
||||
return self._get(_driver.Driver, driver)
|
||||
|
||||
def list_driver_vendor_passthru(self, driver):
|
||||
"""Get driver's vendor_passthru methods.
|
||||
|
||||
:param driver: The value can be the name of a driver or a
|
||||
:class:`~openstack.baremetal.v1.driver.Driver` instance.
|
||||
|
||||
:returns: One :dict: of vendor methods with corresponding usages
|
||||
:raises: :class:`~openstack.exceptions.ResourceNotFound` when no
|
||||
driver matching the name could be found.
|
||||
"""
|
||||
driver = self.get_driver(driver)
|
||||
return driver.list_vendor_passthru(self)
|
||||
|
||||
def call_driver_vendor_passthru(self, driver,
|
||||
verb: str, method: str, body=None):
|
||||
"""Call driver's vendor_passthru method.
|
||||
|
||||
:param driver: The value can be the name of a driver or a
|
||||
:class:`~openstack.baremetal.v1.driver.Driver` instance.
|
||||
:param verb: One of GET, POST, PUT, DELETE,
|
||||
depending on the driver and method.
|
||||
:param method: Name of vendor method.
|
||||
:param body: passed to the vendor function as json body.
|
||||
|
||||
:returns: Server response
|
||||
"""
|
||||
driver = self.get_driver(driver)
|
||||
return driver.call_vendor_passthru(self, verb, method, body)
|
||||
|
||||
def nodes(self, details=False, **query):
|
||||
"""Retrieve a generator of nodes.
|
||||
|
||||
|
@@ -10,7 +10,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack.baremetal.v1 import _common
|
||||
from openstack import exceptions
|
||||
from openstack import resource
|
||||
from openstack import utils
|
||||
|
||||
|
||||
class Driver(resource.Resource):
|
||||
@@ -117,3 +120,60 @@ class Driver(resource.Resource):
|
||||
#: Enabled vendor interface implementations.
|
||||
#: Introduced in API microversion 1.30.
|
||||
enabled_vendor_interfaces = resource.Body("enabled_vendor_interfaces")
|
||||
|
||||
def list_vendor_passthru(self, session):
|
||||
"""Fetch vendor specific methods exposed by driver
|
||||
|
||||
:param session: The session to use for making this request.
|
||||
:returns: A dict of the available vendor passthru methods for driver.
|
||||
Method names keys and corresponding usages in dict form as values
|
||||
Usage dict properties:
|
||||
* ``async``: bool # Is passthru function invoked asynchronously
|
||||
* ``attach``: bool # Is return value attached to response object
|
||||
* ``description``: str # Description of what the method does
|
||||
* ``http_methods``: list # List of HTTP methods supported
|
||||
"""
|
||||
session = self._get_session(session)
|
||||
request = self._prepare_request()
|
||||
request.url = utils.urljoin(
|
||||
request.url, 'vendor_passthru', 'methods')
|
||||
response = session.get(request.url, headers=request.headers)
|
||||
|
||||
msg = ("Failed to list list vendor_passthru methods for {driver_name}"
|
||||
.format(driver_name=self.name))
|
||||
exceptions.raise_from_response(response, error_message=msg)
|
||||
return response.json()
|
||||
|
||||
def call_vendor_passthru(self, session,
|
||||
verb: str, method: str, body: dict = None):
|
||||
"""Call a vendor specific passthru method
|
||||
|
||||
Contents of body are params passed to the hardware driver
|
||||
function. Validation happens there. Missing parameters, or
|
||||
excess parameters will cause the request to be rejected
|
||||
|
||||
:param session: The session to use for making this request.
|
||||
:param method: Vendor passthru method name.
|
||||
:param verb: One of GET, POST, PUT, DELETE,
|
||||
depending on the driver and method.
|
||||
:param body: passed to the vendor function as json body.
|
||||
:raises: :exc:`ValueError` if :data:`verb` is not one of
|
||||
GET, POST, PUT, DELETE
|
||||
:returns: response of method call.
|
||||
"""
|
||||
if verb.upper() not in ['GET', 'PUT', 'POST', 'DELETE']:
|
||||
raise ValueError('Invalid verb: {}'.format(verb))
|
||||
|
||||
session = self._get_session(session)
|
||||
request = self._prepare_request()
|
||||
request.url = utils.urljoin(
|
||||
request.url, f'vendor_passthru?method={method}')
|
||||
call = getattr(session, verb.lower())
|
||||
response = call(
|
||||
request.url, json=body, headers=request.headers,
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||
|
||||
msg = ("Failed call to method {method} on driver {driver_name}"
|
||||
.format(method=method, driver_name=self.name))
|
||||
exceptions.raise_from_response(response, error_message=msg)
|
||||
return response
|
||||
|
@@ -10,7 +10,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from keystoneauth1 import adapter
|
||||
|
||||
from openstack.baremetal.v1 import _common
|
||||
from openstack.baremetal.v1 import driver
|
||||
from openstack import exceptions
|
||||
from openstack.tests.unit import base
|
||||
|
||||
|
||||
@@ -63,3 +69,63 @@ class TestDriver(base.TestCase):
|
||||
self.assertEqual(FAKE['hosts'], sot.hosts)
|
||||
self.assertEqual(FAKE['links'], sot.links)
|
||||
self.assertEqual(FAKE['properties'], sot.properties)
|
||||
|
||||
@mock.patch.object(exceptions, 'raise_from_response', mock.Mock())
|
||||
def test_list_vendor_passthru(self):
|
||||
self.session = mock.Mock(spec=adapter.Adapter)
|
||||
sot = driver.Driver(**FAKE)
|
||||
fake_vendor_passthru_info = {
|
||||
'fake_vendor_method': {
|
||||
'async': True,
|
||||
'attach': False,
|
||||
'description': "Fake function that does nothing in background",
|
||||
'http_methods': ['GET', 'PUT', 'POST', 'DELETE']
|
||||
}
|
||||
}
|
||||
self.session.get.return_value.json.return_value = (
|
||||
fake_vendor_passthru_info)
|
||||
result = sot.list_vendor_passthru(self.session)
|
||||
self.session.get.assert_called_once_with(
|
||||
'drivers/{driver_name}/vendor_passthru/methods'.format(
|
||||
driver_name=FAKE["name"]),
|
||||
headers=mock.ANY)
|
||||
self.assertEqual(result, fake_vendor_passthru_info)
|
||||
|
||||
@mock.patch.object(exceptions, 'raise_from_response', mock.Mock())
|
||||
def test_call_vendor_passthru(self):
|
||||
self.session = mock.Mock(spec=adapter.Adapter)
|
||||
sot = driver.Driver(**FAKE)
|
||||
# GET
|
||||
sot.call_vendor_passthru(self.session, 'GET', 'fake_vendor_method')
|
||||
self.session.get.assert_called_once_with(
|
||||
'drivers/{}/vendor_passthru?method={}'.format(
|
||||
FAKE["name"], 'fake_vendor_method'),
|
||||
json=None,
|
||||
headers=mock.ANY,
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||
# PUT
|
||||
sot.call_vendor_passthru(self.session, 'PUT', 'fake_vendor_method',
|
||||
body={"fake_param_key": "fake_param_value"})
|
||||
self.session.put.assert_called_once_with(
|
||||
'drivers/{}/vendor_passthru?method={}'.format(
|
||||
FAKE["name"], 'fake_vendor_method'),
|
||||
json={"fake_param_key": "fake_param_value"},
|
||||
headers=mock.ANY,
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||
# POST
|
||||
sot.call_vendor_passthru(self.session, 'POST', 'fake_vendor_method',
|
||||
body={"fake_param_key": "fake_param_value"})
|
||||
self.session.post.assert_called_once_with(
|
||||
'drivers/{}/vendor_passthru?method={}'.format(
|
||||
FAKE["name"], 'fake_vendor_method'),
|
||||
json={"fake_param_key": "fake_param_value"},
|
||||
headers=mock.ANY,
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||
# DELETE
|
||||
sot.call_vendor_passthru(self.session, 'DELETE', 'fake_vendor_method')
|
||||
self.session.delete.assert_called_once_with(
|
||||
'drivers/{}/vendor_passthru?method={}'.format(
|
||||
FAKE["name"], 'fake_vendor_method'),
|
||||
json=None,
|
||||
headers=mock.ANY,
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||
|
Reference in New Issue
Block a user