Adding root_device_hint plugin

In some cases (eg. DRAC), the driver doesn't provide any information to
figure out the root device during RAID configuration. A workaround for
that is creating the RAID disks in 2 runs: one run for the root device
and another one for the rest. In between ironic-discoverd can be used to
examine the changes in the block devices.

This patch adds this capability as a plugin to gather the block device
serials, save it temporarly and expose the root device hint.

Change-Id: I71c42cb131a4e8f0b4fe4eb2f42ac7cde2bceea9
This commit is contained in:
Imre Farkas 2015-02-11 17:51:31 +01:00
parent f656333d52
commit 4e8260aa8f
4 changed files with 156 additions and 0 deletions

View File

@ -308,6 +308,9 @@ Here are some plugins that can be additionally enabled:
reports error, if ``error`` field is set by the ramdisk.
``example``
example plugin logging it's input and output.
``root_device_hint``
gathers block devices from ramdisk and exposes root device in multiple
runs.
Refer to CONTRIBUTING.rst_ for information on how to write your own plugin.
@ -343,6 +346,8 @@ See `1.1.0 release tracking page`_ for details.
See `eDeploy blueprint`_ for details.
* Plugin ``root_device_hint`` for in-band root device discovery.
**Known Issues**
.. _1.1.0 release tracking page: https://bugs.launchpad.net/ironic-discoverd/+milestone/1.1.0

View File

@ -0,0 +1,76 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Gather root device hint from recognized block devices."""
import logging
from ironic_discoverd.plugins import base
LOG = logging.getLogger('ironic_discoverd.plugins.root_device_hint')
class RootDeviceHintHook(base.ProcessingHook):
"""Interact with Instack ramdisk for discovery data processing hooks.
The plugin can figure out the root device in 2 runs. First, it saves the
discovered block device serials in node.extra. The second run will check
the difference between the recently discovered block devices and the
previously saved ones. After saving the root device in node.properties, it
will delete the temporarily saved block device serials in node.extra.
This way, it helps to figure out the root device hint in cases when
otherwise Ironic doesn't have enough information to do so. Such a usecase
is DRAC RAID configuration where the BMC doesn't provide any useful
information about the created RAID disks. Using this plugin immediately
before and after creating the root RAID device will solve the issue of root
device hints.
"""
def before_update(self, node, ports, node_info):
if 'root_device' in node.properties:
LOG.info('Root device is already known for the node.')
return [], {}
if 'block_devices' in node.extra:
# Compare previously discovered devices with the current ones
previous_devices = node.extra['block_devices']['serials']
current_devices = node_info['block_devices']['serials']
new_devices = [device for device in current_devices
if device not in previous_devices]
if len(new_devices) > 1:
LOG.warning('Root device cannot be identified because '
'multiple new devices were found.')
return [], {}
elif len(new_devices) == 0:
LOG.warning('No new devices were found.')
return [], {}
return [
{'op': 'remove',
'path': '/extra/block_devices'},
{'op': 'add',
'path': '/properties/root_device',
'value': {'serial': new_devices[0]}}
], {}
else:
# No previously discovered devices - save the discoverd block
# devices in node.extra
return [
{'op': 'add',
'path': '/extra/block_devices',
'value': node_info['block_devices']}
], {}

View File

@ -0,0 +1,74 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ironic_discoverd.plugins import root_device_hint
from ironic_discoverd.test import base as test_base
class TestRootDeviceHint(test_base.NodeTest):
def setUp(self):
super(TestRootDeviceHint, self).setUp()
self.hook = root_device_hint.RootDeviceHintHook()
def test_no_previous_block_devices(self):
node_info = {'block_devices': {'serials': ['foo', 'bar']}}
node_patches, _ = self.hook.before_update(self.node, None, node_info)
self.assertEqual('add',
node_patches[0]['op'])
self.assertEqual('/extra/block_devices',
node_patches[0]['path'])
self.assertEqual(node_info['block_devices'],
node_patches[0]['value'])
def test_root_device_found(self):
self.node.extra['block_devices'] = {'serials': ['foo', 'bar']}
node_info = {'block_devices': {'serials': ['foo', 'baz']}}
self.hook.before_processing(node_info)
node_patches, _ = self.hook.before_update(self.node, None, node_info)
self.assertEqual('remove',
node_patches[0]['op'])
self.assertEqual('/extra/block_devices',
node_patches[0]['path'])
self.assertEqual('add',
node_patches[1]['op'])
self.assertEqual('/properties/root_device',
node_patches[1]['path'])
self.assertEqual({'serial': 'baz'},
node_patches[1]['value'])
def test_root_device_already_exposed(self):
self.node.properties['root_device'] = {'serial': 'foo'}
node_info = {'block_devices': {'serials': ['foo', 'baz']}}
self.hook.before_processing(node_info)
node_patches, _ = self.hook.before_update(self.node, None, node_info)
self.assertEqual(0, len(node_patches))
def test_multiple_new_devices(self):
self.node.extra['block_devices'] = {'serials': ['foo', 'bar']}
node_info = {'block_devices': {'serials': ['foo', 'baz', 'qux']}}
self.hook.before_processing(node_info)
node_patches, _ = self.hook.before_update(self.node, None, node_info)
self.assertEqual(0, len(node_patches))
def test_no_new_devices(self):
self.node.extra['block_devices'] = {'serials': ['foo', 'bar']}
node_info = {'block_devices': {'serials': ['foo', 'bar']}}
self.hook.before_processing(node_info)
node_patches, _ = self.hook.before_update(self.node, None, node_info)
self.assertEqual(0, len(node_patches))

View File

@ -32,6 +32,7 @@ setup(
"ramdisk_error = ironic_discoverd.plugins.standard:RamdiskErrorHook",
"example = ironic_discoverd.plugins.example:ExampleProcessingHook",
"edeploy = ironic_discoverd.plugins.edeploy:eDeployHook",
"root_device_hint = ironic_discoverd.plugins.root_device_hint:RootDeviceHintHook",
],
},
classifiers = [