diff options
author | Zuul <zuul@review.opendev.org> | 2023-01-06 12:00:17 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2023-01-06 12:00:17 +0000 |
commit | 7be9046021301b56c524eb7f91dd74cff0645571 (patch) | |
tree | 1d5efddaabf25efa69e2a48b9a96fa57f7034460 | |
parent | bed680951a55d2a0782c1e9abda6cd954045a577 (diff) | |
parent | 973d3b6ea671b15583925eaa93ea988d16734499 (diff) | |
download | ironic-7be9046021301b56c524eb7f91dd74cff0645571.tar.gz |
Merge "Enable alternative storage for inventory"
-rw-r--r-- | ironic/common/swift.py | 25 | ||||
-rw-r--r-- | ironic/conf/inspector.py | 11 | ||||
-rw-r--r-- | ironic/drivers/modules/inspector.py | 46 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/test_inspector.py | 46 |
4 files changed, 123 insertions, 5 deletions
diff --git a/ironic/common/swift.py b/ironic/common/swift.py index 8a98c32d2..dde94fb18 100644 --- a/ironic/common/swift.py +++ b/ironic/common/swift.py @@ -111,6 +111,31 @@ class SwiftAPI(object): return obj_uuid + def create_object_from_data(self, object, data, container): + """Uploads a given string to Swift. + + :param object: The name of the object in Swift + :param data: string data to put in the object + :param container: The name of the container for the object. + Defaults to the value set in the configuration options. + :returns: The Swift UUID of the object + :raises: utils.Error, if any operation with Swift fails. + """ + try: + self.connection.put_container(container) + except swift_exceptions.ClientException as e: + operation = _("put container") + raise exception.SwiftOperationError(operation=operation, error=e) + + try: + obj_uuid = self.connection.create_object( + container, object, data=data) + except swift_exceptions.ClientException as e: + operation = _("put object") + raise exception.SwiftOperationError(operation=operation, error=e) + + return obj_uuid + def get_temp_url(self, container, obj, timeout): """Returns the temp url for the given Swift object. diff --git a/ironic/conf/inspector.py b/ironic/conf/inspector.py index a7f89c994..bee6a3e67 100644 --- a/ironic/conf/inspector.py +++ b/ironic/conf/inspector.py @@ -39,6 +39,17 @@ opts = [ 'managed by ironic. Set this to True if your ' 'installation of ironic-inspector does not have a ' 'separate PXE boot environment.')), + cfg.StrOpt('inventory_data_backend', + help=_('The storage backend for storing introspection data.'), + choices=[('none', _('introspection data will not be stored')), + ('database', _('introspection data stored in an SQL ' + 'database')), + ('swift', _('introspection data stored in Swift'))], + default='database'), + cfg.StrOpt('swift_inventory_data_container', + default='introspection_data_container', + help=_('The Swift introspection data container to store ' + 'the inventory data.')), ] diff --git a/ironic/drivers/modules/inspector.py b/ironic/drivers/modules/inspector.py index 45f3a87f5..20911cbaa 100644 --- a/ironic/drivers/modules/inspector.py +++ b/ironic/drivers/modules/inspector.py @@ -28,6 +28,7 @@ from ironic.common import exception from ironic.common.i18n import _ from ironic.common import keystone from ironic.common import states +from ironic.common import swift from ironic.common import utils from ironic.conductor import periodics from ironic.conductor import task_manager @@ -43,6 +44,7 @@ LOG = logging.getLogger(__name__) _INSPECTOR_SESSION = None # Internal field to mark whether ironic or inspector manages boot for the node _IRONIC_MANAGES_BOOT = 'inspector_manage_boot' +_OBJECT_NAME_PREFIX = 'inspector_data' def _get_inspector_session(**kwargs): @@ -365,14 +367,31 @@ def _check_status(task): _inspection_error_handler(task, error) elif status.is_finished: _clean_up(task) + # If store_data == 'none', do not store the data + store_data = CONF.inspector.inventory_data_backend + if store_data == 'none': + LOG.debug('Introspection data storage is disabled, the data will ' + 'not be saved for node %(node)s', {'node': node.uuid}) + return introspection_data = inspector_client.get_introspection_data( node.uuid, processed=True) inventory_data = introspection_data.pop("inventory") plugin_data = introspection_data - node_inventory.NodeInventory( - node_id=node.id, - inventory_data=inventory_data, - plugin_data=plugin_data).create() + if store_data == 'database': + node_inventory.NodeInventory( + node_id=node.id, + inventory_data=inventory_data, + plugin_data=plugin_data).create() + LOG.info('Introspection data was stored in database for node ' + '%(node)s', {'node': node.uuid}) + if store_data == 'swift': + swift_object_name = store_introspection_data( + node_uuid=node.uuid, + inventory_data=inventory_data, + plugin_data=plugin_data) + LOG.info('Introspection data was stored for node %(node)s in Swift' + ' object %(obj_name)s-inventory and %(obj_name)s-plugin', + {'node': node.uuid, 'obj_name': swift_object_name}) def _clean_up(task): @@ -387,3 +406,22 @@ def _clean_up(task): LOG.info('Inspection finished successfully for node %s', task.node.uuid) task.process_event('done') + + +def store_introspection_data(node_uuid, inventory_data, plugin_data): + """Uploads introspection data to Swift. + + :param data: data to store in Swift + :param node_id: ID of the Ironic node that the data came from + :returns: name of the Swift object that the data is stored in + """ + swift_api = swift.SwiftAPI() + swift_object_name = '%s-%s' % (_OBJECT_NAME_PREFIX, node_uuid) + container = CONF.inspector.swift_inventory_data_container + swift_api.create_object_from_data(swift_object_name + '-inventory', + inventory_data, + container) + swift_api.create_object_from_data(swift_object_name + '-plugin', + plugin_data, + container) + return swift_object_name diff --git a/ironic/tests/unit/drivers/modules/test_inspector.py b/ironic/tests/unit/drivers/modules/test_inspector.py index 5eb702e41..00de10189 100644 --- a/ironic/tests/unit/drivers/modules/test_inspector.py +++ b/ironic/tests/unit/drivers/modules/test_inspector.py @@ -19,6 +19,7 @@ import openstack from ironic.common import context from ironic.common import exception from ironic.common import states +from ironic.common import swift from ironic.common import utils from ironic.conductor import task_manager from ironic.drivers.modules import inspect_utils @@ -553,7 +554,9 @@ class CheckStatusTestCase(BaseTestCase): self.task) self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task) - def test_status_ok_store_inventory(self, mock_client): + def test_status_ok_store_inventory_in_db(self, mock_client): + CONF.set_override('inventory_data_backend', 'database', + group='inspector') mock_get = mock_client.return_value.get_introspection mock_get.return_value = mock.Mock(is_finished=True, error=None, @@ -571,7 +574,47 @@ class CheckStatusTestCase(BaseTestCase): self.assertEqual({"disks": [{"name": "/dev/vda"}]}, stored["plugin_data"]) + @mock.patch.object(swift, 'SwiftAPI', autospec=True) + def test_status_ok_store_inventory_in_swift(self, + swift_api_mock, mock_client): + CONF.set_override('inventory_data_backend', 'swift', group='inspector') + CONF.set_override( + 'swift_inventory_data_container', 'introspection_data', + group='inspector') + mock_get = mock_client.return_value.get_introspection + mock_get.return_value = mock.Mock(is_finished=True, + error=None, + spec=['is_finished', 'error']) + mock_get_data = mock_client.return_value.get_introspection_data + fake_inventory_data = {"cpu": "amd"} + fake_plugin_data = {"disks": [{"name": "/dev/vda"}]} + mock_get_data.return_value = { + "inventory": fake_inventory_data, **fake_plugin_data} + swift_obj_mock = swift_api_mock.return_value + object_name = 'inspector_data-' + str(self.node.uuid) + inspector._check_status(self.task) + mock_get.assert_called_once_with(self.node.uuid) + mock_get_data.assert_called_once_with(self.node.uuid, processed=True) + container = 'introspection_data' + swift_obj_mock.create_object_from_data.assert_has_calls([ + mock.call(object_name + '-inventory', fake_inventory_data, + container), + mock.call(object_name + '-plugin', fake_plugin_data, container)]) + + def test_status_ok_store_inventory_nostore(self, mock_client): + CONF.set_override('inventory_data_backend', 'none', group='inspector') + mock_get = mock_client.return_value.get_introspection + mock_get.return_value = mock.Mock(is_finished=True, + error=None, + spec=['is_finished', 'error']) + mock_get_data = mock_client.return_value.get_introspection_data + inspector._check_status(self.task) + mock_get.assert_called_once_with(self.node.uuid) + mock_get_data.assert_not_called() + def test_status_error_dont_store_inventory(self, mock_client): + CONF.set_override('inventory_data_backend', 'database', + group='inspector') mock_get = mock_client.return_value.get_introspection mock_get.return_value = mock.Mock(is_finished=True, error='boom', @@ -593,4 +636,5 @@ class InspectHardwareAbortTestCase(BaseTestCase): mock_abort = mock_client.return_value.abort_introspection mock_abort.side_effect = RuntimeError('boom') self.assertRaises(RuntimeError, self.iface.abort, self.task) + mock_abort.assert_called_once_with(self.node.uuid) |