Introspection on stored data
this commit introduces Inspector service endpoint ``POST@/v1/introspection/<uuid>/data/unprocessed`` to the client code to be able to reprocess stored introspection data Change-Id: I45d57eb903c02e50a3ce98a9fb3e6f7d8d36d79b Related-bug: #1525237
This commit is contained in:
parent
d6585166c3
commit
a89a6043ee
15
README.rst
15
README.rst
@ -140,6 +140,21 @@ CLI::
|
|||||||
|
|
||||||
$ openstack baremetal introspection abort UUID
|
$ openstack baremetal introspection abort UUID
|
||||||
|
|
||||||
|
Reprocess stored introspection data
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``client.reprocess(uuid)``
|
||||||
|
|
||||||
|
* ``uuid`` - Ironic node UUID.
|
||||||
|
|
||||||
|
CLI::
|
||||||
|
|
||||||
|
$ openstack baremetal introspection reprocess UUID
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This feature requires Swift store to be enabled for **Ironic Inspector**
|
||||||
|
by setting ``[processing]store_data`` configuration option to ``swift``.
|
||||||
|
|
||||||
Introspection Rules API
|
Introspection Rules API
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -104,6 +104,19 @@ class StartCommand(lister.Lister):
|
|||||||
return self.COLUMNS, result
|
return self.COLUMNS, result
|
||||||
|
|
||||||
|
|
||||||
|
class ReprocessCommand(command.Command):
|
||||||
|
"""Reprocess stored introspection data"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ReprocessCommand, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('uuid', help='baremetal node UUID')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = self.app.client_manager.baremetal_introspection
|
||||||
|
client.reprocess(parsed_args.uuid)
|
||||||
|
|
||||||
|
|
||||||
class StatusCommand(show.ShowOne):
|
class StatusCommand(show.ShowOne):
|
||||||
"""Get introspection status."""
|
"""Get introspection status."""
|
||||||
|
|
||||||
|
@ -14,9 +14,12 @@
|
|||||||
import eventlet
|
import eventlet
|
||||||
eventlet.monkey_patch()
|
eventlet.monkey_patch()
|
||||||
|
|
||||||
|
import json
|
||||||
|
import mock
|
||||||
import requests
|
import requests
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from ironic_inspector.common import swift
|
||||||
from ironic_inspector.test import functional
|
from ironic_inspector.test import functional
|
||||||
|
|
||||||
import ironic_inspector_client as client
|
import ironic_inspector_client as client
|
||||||
@ -26,6 +29,7 @@ class TestV1PythonAPI(functional.Base):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestV1PythonAPI, self).setUp()
|
super(TestV1PythonAPI, self).setUp()
|
||||||
self.client = client.ClientV1()
|
self.client = client.ClientV1()
|
||||||
|
functional.cfg.CONF.set_override('store_data', '', 'processing')
|
||||||
|
|
||||||
def test_introspect_get_status(self):
|
def test_introspect_get_status(self):
|
||||||
self.client.introspect(self.uuid)
|
self.client.introspect(self.uuid)
|
||||||
@ -47,6 +51,53 @@ class TestV1PythonAPI(functional.Base):
|
|||||||
status = self.client.get_status(self.uuid)
|
status = self.client.get_status(self.uuid)
|
||||||
self.assertEqual({'finished': True, 'error': None}, status)
|
self.assertEqual({'finished': True, 'error': None}, status)
|
||||||
|
|
||||||
|
@mock.patch.object(swift, 'store_introspection_data', autospec=True)
|
||||||
|
@mock.patch.object(swift, 'get_introspection_data', autospec=True)
|
||||||
|
def test_reprocess_stored_introspection_data(self, get_mock,
|
||||||
|
store_mock):
|
||||||
|
functional.cfg.CONF.set_override('store_data', 'swift', 'processing')
|
||||||
|
port_create_call = mock.call(node_uuid=self.uuid,
|
||||||
|
address='11:22:33:44:55:66')
|
||||||
|
get_mock.return_value = json.dumps(self.data)
|
||||||
|
|
||||||
|
# assert reprocessing doesn't work before introspection
|
||||||
|
self.assertRaises(client.ClientError, self.client.reprocess,
|
||||||
|
self.uuid)
|
||||||
|
|
||||||
|
self.client.introspect(self.uuid)
|
||||||
|
eventlet.greenthread.sleep(functional.DEFAULT_SLEEP)
|
||||||
|
self.cli.node.set_power_state.assert_called_once_with(self.uuid,
|
||||||
|
'reboot')
|
||||||
|
status = self.client.get_status(self.uuid)
|
||||||
|
self.assertEqual({'finished': False, 'error': None},
|
||||||
|
status)
|
||||||
|
|
||||||
|
res = self.call_continue(self.data)
|
||||||
|
self.assertEqual({'uuid': self.uuid}, res)
|
||||||
|
eventlet.greenthread.sleep(functional.DEFAULT_SLEEP)
|
||||||
|
|
||||||
|
status = self.client.get_status(self.uuid)
|
||||||
|
self.assertEqual({'finished': True, 'error': None},
|
||||||
|
status)
|
||||||
|
self.cli.port.create.assert_has_calls([port_create_call],
|
||||||
|
any_order=True)
|
||||||
|
self.assertFalse(get_mock.called)
|
||||||
|
self.assertTrue(store_mock.called)
|
||||||
|
|
||||||
|
res = self.client.reprocess(self.uuid)
|
||||||
|
self.assertEqual(202, res.status_code)
|
||||||
|
self.assertEqual('', res.text)
|
||||||
|
eventlet.greenthread.sleep(functional.DEFAULT_SLEEP)
|
||||||
|
self.assertEqual({'finished': True, 'error': None},
|
||||||
|
status)
|
||||||
|
|
||||||
|
self.cli.port.create.assert_has_calls([port_create_call,
|
||||||
|
port_create_call],
|
||||||
|
any_order=True)
|
||||||
|
self.assertTrue(get_mock.called)
|
||||||
|
# incoming, processing, reapplying data
|
||||||
|
self.assertEqual(3, store_mock.call_count)
|
||||||
|
|
||||||
def test_abort_introspection(self):
|
def test_abort_introspection(self):
|
||||||
# assert abort doesn't work before introspect request
|
# assert abort doesn't work before introspect request
|
||||||
self.assertRaises(client.ClientError, self.client.abort,
|
self.assertRaises(client.ClientError, self.client.abort,
|
||||||
|
@ -104,6 +104,21 @@ class TestIntrospect(BaseTest):
|
|||||||
for uuid in uuids]
|
for uuid in uuids]
|
||||||
self.assertEqual(calls, self.client.introspect.call_args_list)
|
self.assertEqual(calls, self.client.introspect.call_args_list)
|
||||||
|
|
||||||
|
def test_reprocess(self):
|
||||||
|
node = 'uuid1'
|
||||||
|
arglist = [node]
|
||||||
|
verifylist = [('uuid', node)]
|
||||||
|
response_mock = mock.Mock(status_code=202, content=b'')
|
||||||
|
self.client.reprocess.return_value = response_mock
|
||||||
|
|
||||||
|
cmd = shell.ReprocessCommand(self.app, None)
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(cmd, arglist, verifylist)
|
||||||
|
result = cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
self.client.reprocess.assert_called_once_with(node)
|
||||||
|
self.assertIs(None, result)
|
||||||
|
|
||||||
def test_wait(self):
|
def test_wait(self):
|
||||||
nodes = ['uuid1', 'uuid2', 'uuid3']
|
nodes = ['uuid1', 'uuid2', 'uuid3']
|
||||||
arglist = ['--wait'] + nodes
|
arglist = ['--wait'] + nodes
|
||||||
|
@ -101,6 +101,20 @@ class TestIntrospect(BaseTest):
|
|||||||
params={'new_ipmi_username': 'u', 'new_ipmi_password': 'p'})
|
params={'new_ipmi_username': 'u', 'new_ipmi_password': 'p'})
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(http.BaseClient, 'request')
|
||||||
|
class TestReprocess(BaseTest):
|
||||||
|
def test(self, mock_req):
|
||||||
|
self.get_client().reprocess(self.uuid)
|
||||||
|
mock_req.assert_called_once_with(
|
||||||
|
'post',
|
||||||
|
'/introspection/%s/data/unprocessed' % self.uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_invalid_input(self, mock_req):
|
||||||
|
self.assertRaises(TypeError, self.get_client().reprocess, 42)
|
||||||
|
self.assertFalse(mock_req.called)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(http.BaseClient, 'request')
|
@mock.patch.object(http.BaseClient, 'request')
|
||||||
class TestGetStatus(BaseTest):
|
class TestGetStatus(BaseTest):
|
||||||
def test(self, mock_req):
|
def test(self, mock_req):
|
||||||
|
@ -23,7 +23,7 @@ from ironic_inspector_client.common.i18n import _
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_API_VERSION = (1, 0)
|
DEFAULT_API_VERSION = (1, 0)
|
||||||
MAX_API_VERSION = (1, 3)
|
MAX_API_VERSION = (1, 4)
|
||||||
|
|
||||||
# using huge timeout by default, as precise timeout should be set in
|
# using huge timeout by default, as precise timeout should be set in
|
||||||
# ironic-inspector settings
|
# ironic-inspector settings
|
||||||
@ -74,6 +74,23 @@ class ClientV1(http.BaseClient):
|
|||||||
'new_ipmi_password': new_ipmi_password}
|
'new_ipmi_password': new_ipmi_password}
|
||||||
self.request('post', '/introspection/%s' % uuid, params=params)
|
self.request('post', '/introspection/%s' % uuid, params=params)
|
||||||
|
|
||||||
|
def reprocess(self, uuid):
|
||||||
|
"""Reprocess stored introspection data.
|
||||||
|
|
||||||
|
:param uuid: node UUID.
|
||||||
|
:raises: ClientError on error reported from the server.
|
||||||
|
:raises: VersionNotSupported if requested api_version is not supported.
|
||||||
|
:raises: *requests* library exception on connection problems.
|
||||||
|
:raises: TypeError if uuid is not a string.
|
||||||
|
"""
|
||||||
|
if not isinstance(uuid, six.string_types):
|
||||||
|
raise TypeError(_("Expected string for uuid argument, got"
|
||||||
|
" %r instead") % uuid)
|
||||||
|
|
||||||
|
return self.request('post',
|
||||||
|
'/introspection/%s/data/unprocessed' %
|
||||||
|
uuid)
|
||||||
|
|
||||||
def get_status(self, uuid):
|
def get_status(self, uuid):
|
||||||
"""Get introspection status for a node.
|
"""Get introspection status for a node.
|
||||||
|
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Introduced command "openstack baremetal introspection reprocess <UUID>"
|
||||||
|
to reprocess stored introspection data
|
@ -23,6 +23,7 @@ openstack.cli.extension =
|
|||||||
openstack.baremetal_introspection.v1 =
|
openstack.baremetal_introspection.v1 =
|
||||||
baremetal_introspection_start = ironic_inspector_client.shell:StartCommand
|
baremetal_introspection_start = ironic_inspector_client.shell:StartCommand
|
||||||
baremetal_introspection_status = ironic_inspector_client.shell:StatusCommand
|
baremetal_introspection_status = ironic_inspector_client.shell:StatusCommand
|
||||||
|
baremetal_introspection_reprocess = ironic_inspector_client.shell:ReprocessCommand
|
||||||
baremetal_introspection_abort = ironic_inspector_client.shell:AbortCommand
|
baremetal_introspection_abort = ironic_inspector_client.shell:AbortCommand
|
||||||
baremetal_introspection_data_save = ironic_inspector_client.shell:DataSaveCommand
|
baremetal_introspection_data_save = ironic_inspector_client.shell:DataSaveCommand
|
||||||
baremetal_introspection_rule_import = ironic_inspector_client.shell:RuleImportCommand
|
baremetal_introspection_rule_import = ironic_inspector_client.shell:RuleImportCommand
|
||||||
|
Loading…
Reference in New Issue
Block a user