Add support for reset_interfaces in node's PATCH

Change-Id: I52ee891d6549827d9a055f23d475fe42f422c4d2
Depends-On: https://review.openstack.org/582951
Story: #2002868
Task: #22822
This commit is contained in:
Dmitry Tantsur 2018-07-17 14:26:56 +02:00
parent d401c94848
commit 7c77aba2b9
8 changed files with 109 additions and 19 deletions

View File

@ -209,7 +209,7 @@ class Manager(object):
return self.__list(url, response_key=response_key) return self.__list(url, response_key=response_key)
def _update(self, resource_id, patch, method='PATCH', def _update(self, resource_id, patch, method='PATCH',
os_ironic_api_version=None): os_ironic_api_version=None, params=None):
"""Update a resource. """Update a resource.
:param resource_id: Resource identifier. :param resource_id: Resource identifier.
@ -217,6 +217,7 @@ class Manager(object):
:param method: Name of the method for the request. :param method: Name of the method for the request.
:param os_ironic_api_version: String version (e.g. "1.35") to use for :param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used. the request. If not specified, the client's default is used.
:param params: query parameters to pass.
""" """
url = self._path(resource_id) url = self._path(resource_id)
@ -226,6 +227,8 @@ class Manager(object):
if os_ironic_api_version is not None: if os_ironic_api_version is not None:
kwargs['headers'] = {'X-OpenStack-Ironic-API-Version': kwargs['headers'] = {'X-OpenStack-Ironic-API-Version':
os_ironic_api_version} os_ironic_api_version}
if params:
kwargs['params'] = params
resp, body = self.api.json_request(method, url, **kwargs) resp, body = self.api.json_request(method, url, **kwargs)
# PATCH/PUT requests may not return a body # PATCH/PUT requests may not return a body
if body: if body:

View File

@ -43,7 +43,7 @@ from ironicclient import exc
# http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa # http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa
# for full details. # for full details.
DEFAULT_VER = '1.9' DEFAULT_VER = '1.9'
LAST_KNOWN_API_VERSION = 44 LAST_KNOWN_API_VERSION = 45
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION) LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)

View File

@ -1085,6 +1085,12 @@ class SetBaremetalNode(command.Command):
reset_help=_('Reset the vendor interface to its hardware type ' reset_help=_('Reset the vendor interface to its hardware type '
'default'), 'default'),
) )
parser.add_argument(
'--reset-interfaces',
action='store_true', default=None,
help=_('Reset all interfaces not specified explicitly to their '
'default implementations. Only valid with --driver.'),
)
parser.add_argument( parser.add_argument(
'--resource-class', '--resource-class',
metavar='<resource_class>', metavar='<resource_class>',
@ -1162,6 +1168,9 @@ class SetBaremetalNode(command.Command):
driver = ["driver=%s" % parsed_args.driver] driver = ["driver=%s" % parsed_args.driver]
properties.extend(utils.args_array_to_patch( properties.extend(utils.args_array_to_patch(
'add', driver)) 'add', driver))
if parsed_args.reset_interfaces and not parsed_args.driver:
raise exc.CommandError(
_("--reset-interfaces can only be specified with --driver"))
for iface in SUPPORTED_INTERFACES: for iface in SUPPORTED_INTERFACES:
field = '%s_interface' % iface field = '%s_interface' % iface
@ -1193,7 +1202,9 @@ class SetBaremetalNode(command.Command):
'add', ['instance_info/' + x for x 'add', ['instance_info/' + x for x
in parsed_args.instance_info])) in parsed_args.instance_info]))
if properties: if properties:
baremetal_client.node.update(parsed_args.node, properties) baremetal_client.node.update(
parsed_args.node, properties,
reset_interfaces=parsed_args.reset_interfaces)
elif not parsed_args.target_raid_config: elif not parsed_args.target_raid_config:
self.log.warning("Please specify what to set.") self.log.warning("Please specify what to set.")

View File

@ -1920,7 +1920,8 @@ class TestBaremetalSet(TestBaremetal):
'node_uuid', 'node_uuid',
[{'path': '/properties/path/to/property', [{'path': '/properties/path/to/property',
'value': 'value', 'value': 'value',
'op': 'add'}]) 'op': 'add'}],
reset_interfaces=None)
def test_baremetal_set_multiple_properties(self): def test_baremetal_set_multiple_properties(self):
arglist = [ arglist = [
@ -1948,7 +1949,8 @@ class TestBaremetalSet(TestBaremetal):
'op': 'add'}, 'op': 'add'},
{'path': '/properties/other/path', {'path': '/properties/other/path',
'value': 'value2', 'value': 'value2',
'op': 'add'}] 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_instance_uuid(self): def test_baremetal_set_instance_uuid(self):
@ -1967,7 +1969,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/instance_uuid', 'value': 'xxxxx', 'op': 'add'}] [{'path': '/instance_uuid', 'value': 'xxxxx', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_name(self): def test_baremetal_set_name(self):
@ -1986,7 +1989,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/name', 'value': 'xxxxx', 'op': 'add'}] [{'path': '/name', 'value': 'xxxxx', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_chassis(self): def test_baremetal_set_chassis(self):
@ -2006,7 +2010,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/chassis_uuid', 'value': chassis, 'op': 'add'}] [{'path': '/chassis_uuid', 'value': chassis, 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_driver(self): def test_baremetal_set_driver(self):
@ -2025,9 +2030,48 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/driver', 'value': 'xxxxx', 'op': 'add'}] [{'path': '/driver', 'value': 'xxxxx', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_driver_reset_interfaces(self):
arglist = [
'node_uuid',
'--driver', 'xxxxx',
'--reset-interfaces',
]
verifylist = [
('node', 'node_uuid'),
('driver', 'xxxxx'),
('reset_interfaces', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/driver', 'value': 'xxxxx', 'op': 'add'}],
reset_interfaces=True,
)
def test_reset_interfaces_without_driver(self):
arglist = [
'node_uuid',
'--reset-interfaces',
]
verifylist = [
('node', 'node_uuid'),
('reset_interfaces', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exc.CommandError,
self.cmd.take_action, parsed_args)
self.assertFalse(self.baremetal_mock.node.update.called)
def _test_baremetal_set_hardware_interface(self, interface): def _test_baremetal_set_hardware_interface(self, interface):
arglist = [ arglist = [
'node_uuid', 'node_uuid',
@ -2045,7 +2089,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/%s_interface' % interface, [{'path': '/%s_interface' % interface,
'value': 'xxxxx', 'op': 'add'}] 'value': 'xxxxx', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_bios_interface(self): def test_baremetal_set_bios_interface(self):
@ -2100,7 +2145,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/%s_interface' % interface, 'op': 'remove'}] [{'path': '/%s_interface' % interface, 'op': 'remove'}],
reset_interfaces=None,
) )
def test_baremetal_reset_bios_interface(self): def test_baremetal_reset_bios_interface(self):
@ -2155,7 +2201,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/resource_class', 'value': 'foo', 'op': 'add'}] [{'path': '/resource_class', 'value': 'foo', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_extra(self): def test_baremetal_set_extra(self):
@ -2174,7 +2221,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/extra/foo', 'value': 'bar', 'op': 'add'}] [{'path': '/extra/foo', 'value': 'bar', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_driver_info(self): def test_baremetal_set_driver_info(self):
@ -2193,7 +2241,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/driver_info/foo', 'value': 'bar', 'op': 'add'}] [{'path': '/driver_info/foo', 'value': 'bar', 'op': 'add'}],
reset_interfaces=None,
) )
def test_baremetal_set_instance_info(self): def test_baremetal_set_instance_info(self):
@ -2212,7 +2261,8 @@ class TestBaremetalSet(TestBaremetal):
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/instance_info/foo', 'value': 'bar', 'op': 'add'}] [{'path': '/instance_info/foo', 'value': 'bar', 'op': 'add'}],
reset_interfaces=None,
) )
@mock.patch.object(commonutils, 'get_from_stdin', autospec=True) @mock.patch.object(commonutils, 'get_from_stdin', autospec=True)
@ -2264,7 +2314,8 @@ class TestBaremetalSet(TestBaremetal):
assert_called_once_with('node_uuid', expected_target_raid_config) assert_called_once_with('node_uuid', expected_target_raid_config)
self.baremetal_mock.node.update.assert_called_once_with( self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', 'node_uuid',
[{'path': '/name', 'value': 'xxxxx', 'op': 'add'}]) [{'path': '/name', 'value': 'xxxxx', 'op': 'add'}],
reset_interfaces=None)
@mock.patch.object(commonutils, 'get_from_stdin', autospec=True) @mock.patch.object(commonutils, 'get_from_stdin', autospec=True)
@mock.patch.object(commonutils, 'handle_json_or_file_arg', autospec=True) @mock.patch.object(commonutils, 'handle_json_or_file_arg', autospec=True)

View File

@ -44,8 +44,10 @@ class FakeAPI(object):
self.responses = responses self.responses = responses
self.calls = [] self.calls = []
def _request(self, method, url, headers=None, body=None): def _request(self, method, url, headers=None, body=None, params=None):
call = (method, url, headers or {}, body) call = (method, url, headers or {}, body)
if params:
call += (params,)
self.calls.append(call) self.calls.append(call)
return self.responses[url][method] return self.responses[url][method]

View File

@ -908,6 +908,19 @@ class NodeManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(NEW_DRIVER, node.driver) self.assertEqual(NEW_DRIVER, node.driver)
def test_update_with_reset_interfaces(self):
patch = {'op': 'replace',
'value': NEW_DRIVER,
'path': '/driver'}
node = self.mgr.update(node_id=NODE1['uuid'], patch=patch,
reset_interfaces=True)
expect = [
('PATCH', '/v1/nodes/%s' % NODE1['uuid'],
{}, patch, {'reset_interfaces': True}),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(NEW_DRIVER, node.driver)
def test_update_microversion_override(self): def test_update_microversion_override(self):
patch = {'op': 'replace', patch = {'op': 'replace',
'value': NEW_DRIVER, 'value': NEW_DRIVER,

View File

@ -343,10 +343,14 @@ class NodeManager(base.CreateManager):
return self._delete(resource_id=node_id) return self._delete(resource_id=node_id)
def update(self, node_id, patch, http_method='PATCH', def update(self, node_id, patch, http_method='PATCH',
os_ironic_api_version=None): os_ironic_api_version=None, reset_interfaces=None):
params = {}
if reset_interfaces is not None:
params['reset_interfaces'] = reset_interfaces
return self._update(resource_id=node_id, patch=patch, return self._update(resource_id=node_id, patch=patch,
method=http_method, method=http_method,
os_ironic_api_version=os_ironic_api_version) os_ironic_api_version=os_ironic_api_version,
params=params)
def vendor_passthru(self, node_id, method, args=None, def vendor_passthru(self, node_id, method, args=None,
http_method=None): http_method=None):

View File

@ -0,0 +1,6 @@
---
features:
- |
Adds the new argument ``--reset-interfaces`` to the
``openstack baremetal node set`` command. It can be used together with
``--driver`` to reset all interfaces to their defaults.