Merge "Enable alternative storage for inventory"
This commit is contained in:
commit
7be9046021
@ -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.
|
||||
|
||||
|
@ -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.')),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user