diff --git a/openstack/baremetal/v1/_proxy.py b/openstack/baremetal/v1/_proxy.py index 6ac6728e3..2190fcb70 100644 --- a/openstack/baremetal/v1/_proxy.py +++ b/openstack/baremetal/v1/_proxy.py @@ -1014,6 +1014,25 @@ class Proxy(proxy.Proxy): res = self._get_resource(_node.Node, node) return res.remove_trait(self, trait, ignore_missing=ignore_missing) + def call_node_vendor_passthru(self, node, verb, method, body=None): + """Calls vendor_passthru for a node. + + :param session: The session to use for making this request. + :param verb: The HTTP verb, one of GET, SET, POST, DELETE. + :param method: The method to call using vendor_passthru. + :param body: The JSON body in the HTTP call. + """ + res = self._get_resource(_node.Node, node) + return res.call_vendor_passthru(self, verb, method, body) + + def list_node_vendor_passthru(self, node): + """Lists vendor_passthru for a node. + + :param session: The session to use for making this request. + """ + res = self._get_resource(_node.Node, node) + return res.list_vendor_passthru(self) + def set_node_traits(self, node, traits): """Set traits for a node. diff --git a/openstack/baremetal/v1/node.py b/openstack/baremetal/v1/node.py index 72c50e19a..4aebb5d67 100644 --- a/openstack/baremetal/v1/node.py +++ b/openstack/baremetal/v1/node.py @@ -920,6 +920,55 @@ class Node(_common.ListMixin, resource.Resource): self.traits = traits + def call_vendor_passthru(self, session, verb, method, body=None): + """Call a vendor passthru method. + + :param session: The session to use for making this request. + :param verb: The HTTP verb, one of GET, SET, POST, DELETE. + :param method: The method to call using vendor_passthru. + :param body: The JSON body in the HTTP call. + :returns: The HTTP response. + """ + session = self._get_session(session) + version = self._get_microversion_for(session, 'commit') + request = self._prepare_request(requires_id=True) + request.url = utils.urljoin(request.url, 'vendor_passthru?method={}' + .format(method)) + + call = getattr(session, verb.lower()) + response = call( + request.url, json=body, + headers=request.headers, microversion=version, + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + msg = ("Failed to call vendor_passthru for node {node}, verb {verb}" + " and method {method}" + .format(node=self.id, verb=verb, method=method)) + exceptions.raise_from_response(response, error_message=msg) + + return response + + def list_vendor_passthru(self, session): + """List vendor passthru methods. + + :param session: The session to use for making this request. + :returns: The HTTP response. + """ + session = self._get_session(session) + version = self._get_microversion_for(session, 'fetch') + request = self._prepare_request(requires_id=True) + request.url = utils.urljoin(request.url, 'vendor_passthru/methods') + + response = session.get( + request.url, headers=request.headers, microversion=version, + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + msg = ("Failed to list vendor_passthru methods for node {node}" + .format(node=self.id)) + exceptions.raise_from_response(response, error_message=msg) + + return response.json() + def patch(self, session, patch=None, prepend_key=True, has_body=True, retry_on_conflict=None, base_path=None, reset_interfaces=None): diff --git a/openstack/tests/unit/baremetal/v1/test_node.py b/openstack/tests/unit/baremetal/v1/test_node.py index b00ab3309..c1c90967d 100644 --- a/openstack/tests/unit/baremetal/v1/test_node.py +++ b/openstack/tests/unit/baremetal/v1/test_node.py @@ -840,3 +840,50 @@ class TestNodeWaitForPowerState(base.TestCase): self.assertRaises(exceptions.ResourceTimeout, self.node.wait_for_power_state, self.session, 'power off', timeout=0.001) + + +@mock.patch.object(utils, 'pick_microversion', lambda session, v: v) +@mock.patch.object(node.Node, 'fetch', lambda self, session: self) +@mock.patch.object(exceptions, 'raise_from_response', mock.Mock()) +class TestNodePassthru(object): + def setUp(self): + super(TestNodePassthru, self).setUp() + self.node = node.Node(**FAKE) + self.session = node.Mock(spec=adapter.Adapter, + default_microversion='1.37') + self.session.log = mock.Mock() + + def test_get_passthru(self): + self.node.call_vendor_passthru(self.session, "GET", "test_method") + self.session.get.assert_called_once_with( + 'nodes/%s/vendor_passthru?method=test_method' % self.node.id, + headers=mock.ANY, microversion='1.37', + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + def test_post_passthru(self): + self.node.call_vendor_passthru(self.session, "POST", "test_method") + self.session.post.assert_called_once_with( + 'nodes/%s/vendor_passthru?method=test_method' % self.node.id, + headers=mock.ANY, microversion='1.37', + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + def test_put_passthru(self): + self.node.call_vendor_passthru(self.session, "PUT", "test_method") + self.session.put.assert_called_once_with( + 'nodes/%s/vendor_passthru?method=test_method' % self.node.id, + headers=mock.ANY, microversion='1.37', + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + def test_delete_passthru(self): + self.node.call_vendor_passthru(self.session, "DELETE", "test_method") + self.session.delete.assert_called_once_with( + 'nodes/%s/vendor_passthru?method=test_method' % self.node.id, + headers=mock.ANY, microversion='1.37', + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + def test_list_passthru(self): + self.node.list_vendor_passthru(self.session) + self.session.get.assert_called_once_with( + 'nodes/%s/vendor_passthru/methods' % self.node.id, + headers=mock.ANY, microversion='1.37', + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) diff --git a/releasenotes/notes/add-node-vendor_passthru-29b384cadf795b48.yaml b/releasenotes/notes/add-node-vendor_passthru-29b384cadf795b48.yaml new file mode 100644 index 000000000..682ee4943 --- /dev/null +++ b/releasenotes/notes/add-node-vendor_passthru-29b384cadf795b48.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add node vendor_passthru interface for Ironic API.