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:
parent
f656333d52
commit
4e8260aa8f
|
@ -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
|
||||
|
|
|
@ -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']}
|
||||
], {}
|
|
@ -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))
|
1
setup.py
1
setup.py
|
@ -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 = [
|
||||
|
|
Loading…
Reference in New Issue