Add reset_interfaces argument to patch_node
Ironic uses reset_interfaces option when patching a node to reset all the hardware interfaces of a node. This patch adds that argument to the patch_node method in the baremetal module and introduces a patch method under the node module to be able to evaluate the reset_interfaces parameter. Also, it modifies the _prepare_request method in resource to accept query parameters in urls and build the request uri keeping those into account. Increasing minimum version of mock to 3.0.0 to be able to use testing features not supported before that version, e.g. assert_called_once Change-Id: I8bca403df7d38a7ac1d066c5f1d7e2bff1deb054
This commit is contained in:
parent
25d9aa7de8
commit
13c6bc2bd9
@ -17,7 +17,7 @@ jsonpointer==1.13
|
||||
jsonschema==2.6.0
|
||||
keystoneauth1==3.18.0
|
||||
linecache2==1.0.0
|
||||
mock==2.0.0
|
||||
mock==3.0.0
|
||||
mox3==0.20.0
|
||||
munch==2.1.0
|
||||
netifaces==0.10.4
|
||||
|
@ -294,12 +294,16 @@ class Proxy(proxy.Proxy):
|
||||
res = self._get_resource(_node.Node, node, **attrs)
|
||||
return res.commit(self, retry_on_conflict=retry_on_conflict)
|
||||
|
||||
def patch_node(self, node, patch, retry_on_conflict=True):
|
||||
def patch_node(self, node, patch, reset_interfaces=None,
|
||||
retry_on_conflict=True):
|
||||
"""Apply a JSON patch to the node.
|
||||
|
||||
:param node: The value can be the name or ID of a node or a
|
||||
:class:`~openstack.baremetal.v1.node.Node` instance.
|
||||
:param patch: JSON patch to apply.
|
||||
:param bool reset_interfaces: whether to reset the node hardware
|
||||
interfaces to their defaults. This works only when changing
|
||||
drivers. Added in API microversion 1.45.
|
||||
:param bool retry_on_conflict: Whether to retry HTTP CONFLICT error.
|
||||
Most of the time it can be retried, since it is caused by the node
|
||||
being locked. However, when setting ``instance_id``, this is
|
||||
@ -313,7 +317,8 @@ class Proxy(proxy.Proxy):
|
||||
:rtype: :class:`~openstack.baremetal.v1.node.Node`
|
||||
"""
|
||||
res = self._get_resource(_node.Node, node)
|
||||
return res.patch(self, patch, retry_on_conflict=retry_on_conflict)
|
||||
return res.patch(self, patch, retry_on_conflict=retry_on_conflict,
|
||||
reset_interfaces=reset_interfaces)
|
||||
|
||||
def set_node_provision_state(self, node, target, config_drive=None,
|
||||
clean_steps=None, rescue_password=None,
|
||||
|
@ -833,5 +833,39 @@ class Node(_common.ListMixin, resource.Resource):
|
||||
|
||||
self.traits = traits
|
||||
|
||||
def patch(self, session, patch=None, prepend_key=True, has_body=True,
|
||||
retry_on_conflict=None, base_path=None, reset_interfaces=None):
|
||||
|
||||
if reset_interfaces is not None:
|
||||
# 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")
|
||||
|
||||
session = self._get_session(session)
|
||||
microversion = utils.pick_microversion(session, '1.45')
|
||||
params = [('reset_interfaces', reset_interfaces)]
|
||||
|
||||
request = self._prepare_request(requires_id=True,
|
||||
prepend_key=prepend_key,
|
||||
base_path=base_path, patch=True,
|
||||
params=params)
|
||||
|
||||
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)
|
||||
|
||||
else:
|
||||
return super(Node, self).patch(session, patch=patch,
|
||||
retry_on_conflict=retry_on_conflict)
|
||||
|
||||
|
||||
NodeDetail = Node
|
||||
|
@ -1053,7 +1053,7 @@ class Resource(dict):
|
||||
return body
|
||||
|
||||
def _prepare_request(self, requires_id=None, prepend_key=False,
|
||||
patch=False, base_path=None):
|
||||
patch=False, base_path=None, params=None):
|
||||
"""Prepare a request to be sent to the server
|
||||
|
||||
Create operations don't require an ID, but all others do,
|
||||
@ -1091,6 +1091,10 @@ class Resource(dict):
|
||||
|
||||
uri = utils.urljoin(uri, self.id)
|
||||
|
||||
if params:
|
||||
query_params = six.moves.urllib.parse.urlencode(params)
|
||||
uri += '?' + query_params
|
||||
|
||||
return _Request(uri, body, headers)
|
||||
|
||||
def _translate_response(self, response, has_body=None, error_message=None):
|
||||
|
@ -16,6 +16,7 @@ import mock
|
||||
from openstack.baremetal.v1 import _common
|
||||
from openstack.baremetal.v1 import node
|
||||
from openstack import exceptions
|
||||
from openstack import resource
|
||||
from openstack.tests.unit import base
|
||||
|
||||
# NOTE: Sample data from api-ref doc
|
||||
@ -766,3 +767,39 @@ class TestNodeTraits(base.TestCase):
|
||||
json={'traits': ['CUSTOM_FAKE', 'CUSTOM_REAL', 'CUSTOM_MISSING']},
|
||||
headers=mock.ANY, microversion='1.37',
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
|
||||
|
||||
|
||||
@mock.patch.object(resource.Resource, 'patch', autospec=True)
|
||||
class TestNodePatch(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNodePatch, self).setUp()
|
||||
self.node = node.Node(**FAKE)
|
||||
self.session = mock.Mock(spec=adapter.Adapter,
|
||||
default_microversion=None)
|
||||
self.session.log = mock.Mock()
|
||||
|
||||
def test_node_patch(self, mock_patch):
|
||||
patch = {'path': 'test'}
|
||||
self.node.patch(self.session, patch=patch)
|
||||
mock_patch.assert_called_once()
|
||||
kwargs = mock_patch.call_args.kwargs
|
||||
self.assertEqual(kwargs['patch'], {'path': 'test'})
|
||||
|
||||
@mock.patch.object(resource.Resource, '_prepare_request', autospec=True)
|
||||
@mock.patch.object(resource.Resource, '_commit', autospec=True)
|
||||
def test_node_patch_reset_interfaces(self, mock__commit, mock_prepreq,
|
||||
mock_patch):
|
||||
patch = {'path': 'test'}
|
||||
self.node.patch(self.session, patch=patch, retry_on_conflict=True,
|
||||
reset_interfaces=True)
|
||||
mock_prepreq.assert_called_once()
|
||||
prepreq_kwargs = mock_prepreq.call_args.kwargs
|
||||
self.assertEqual(prepreq_kwargs['params'],
|
||||
[('reset_interfaces', True)])
|
||||
mock__commit.assert_called_once()
|
||||
commit_args = mock__commit.call_args.args
|
||||
commit_kwargs = mock__commit.call_args.kwargs
|
||||
self.assertIn('1.45', commit_args)
|
||||
self.assertEqual(commit_kwargs['retry_on_conflict'], True)
|
||||
mock_patch.assert_not_called()
|
||||
|
@ -1104,6 +1104,27 @@ class TestResource(base.TestCase):
|
||||
self.assertEqual([{'op': 'add', 'path': '/x', 'value': 1}],
|
||||
result.body)
|
||||
|
||||
def test__prepare_request_with_patch_params(self):
|
||||
class Test(resource.Resource):
|
||||
commit_jsonpatch = True
|
||||
base_path = "/something"
|
||||
x = resource.Body("x")
|
||||
y = resource.Body("y")
|
||||
|
||||
the_id = "id"
|
||||
sot = Test.existing(id=the_id, x=1, y=2)
|
||||
sot.x = 3
|
||||
|
||||
params = [('foo', 'bar'),
|
||||
('life', 42)]
|
||||
|
||||
result = sot._prepare_request(requires_id=True, patch=True,
|
||||
params=params)
|
||||
|
||||
self.assertEqual("something/id?foo=bar&life=42", result.url)
|
||||
self.assertEqual([{'op': 'replace', 'path': '/x', 'value': 3}],
|
||||
result.body)
|
||||
|
||||
def test__translate_response_no_body(self):
|
||||
class Test(resource.Resource):
|
||||
attr = resource.Header("attr")
|
||||
|
@ -8,7 +8,7 @@ ddt>=1.0.1 # MIT
|
||||
extras>=1.0.0 # MIT
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
jsonschema>=2.6.0 # MIT
|
||||
mock>=2.0.0 # BSD
|
||||
mock>=3.0.0 # BSD
|
||||
prometheus-client>=0.4.2 # Apache-2.0
|
||||
python-subunit>=1.0.0 # Apache-2.0/BSD
|
||||
oslo.config>=6.1.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user