Add eDeploy plugin
It receives eDeploy's hardware discovery to classify hardware according to hardware profiles and extracts the needed information for the configuration of the node. Change-Id: I11b4cf21ec81ce8efd3222597a5226fe260721e2 Implements: blueprint edeploy
This commit is contained in:
parent
07b2f8e415
commit
22a0e24efb
|
@ -322,9 +322,16 @@ See `1.1.0 release tracking page`_ for details.
|
|||
|
||||
**Other Changes**
|
||||
|
||||
* Experimental plugin ``edeploy`` to use with
|
||||
`eDeploy hardware detection and classification utilities
|
||||
<https://pypi.python.org/pypi/hardware>`_.
|
||||
|
||||
See `eDeploy blueprint`_ for details.
|
||||
|
||||
**Known Issues**
|
||||
|
||||
.. _1.1.0 release tracking page: https://bugs.launchpad.net/ironic-discoverd/+milestone/1.1.0
|
||||
.. _eDeploy blueprint: https://blueprints.launchpad.net/ironic-discoverd/+spec/edeploy
|
||||
|
||||
1.0 Series
|
||||
~~~~~~~~~~
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
# 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.
|
||||
|
||||
"""eDeploy hardware detection and classification plugin.
|
||||
|
||||
See https://blueprints.launchpad.net/ironic-discoverd/+spec/edeploy for
|
||||
details on how to use it. Note that this plugin requires a special ramdisk.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from hardware import matcher
|
||||
from hardware import state
|
||||
|
||||
from ironic_discoverd import conf
|
||||
from ironic_discoverd.plugins import base
|
||||
from ironic_discoverd import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger('ironic_discoverd.plugins.edeploy')
|
||||
|
||||
|
||||
class eDeployHook(base.ProcessingHook):
|
||||
"""Interact with eDeploy ramdisk for discovery data processing hooks."""
|
||||
|
||||
def before_processing(self, node_info):
|
||||
"""Hook to run before data processing.
|
||||
|
||||
Finds matching profile in the database.
|
||||
|
||||
:param node_info: raw information sent by the ramdisk, may be modified
|
||||
by the hook.
|
||||
:raises: Error if node_info does not contain extended information
|
||||
:returns: nothing.
|
||||
"""
|
||||
|
||||
if 'data' not in node_info:
|
||||
raise utils.Error(
|
||||
'edeploy plugin: no "data" key in the received JSON')
|
||||
|
||||
LOG.debug('before_processing: %s', node_info['data'])
|
||||
|
||||
hw_items = []
|
||||
for info in node_info['data']:
|
||||
hw_items.append(tuple(info))
|
||||
|
||||
hw_copy = list(hw_items)
|
||||
self._process_data_for_discoverd(hw_copy, node_info)
|
||||
sobj = None
|
||||
|
||||
try:
|
||||
sobj = state.State(lockname=conf.get('edeploy', 'lockname',
|
||||
'/var/lock/discoverd.lock'))
|
||||
sobj.load(conf.get('edeploy', 'configdir', '/etc/edeploy'))
|
||||
prof, var = sobj.find_match(hw_items)
|
||||
var['profile'] = prof
|
||||
node_info['hardware'] = var
|
||||
except Exception as excpt:
|
||||
LOG.warning(
|
||||
'Unable to find a matching hardware profile: %s' % excpt)
|
||||
finally:
|
||||
if sobj:
|
||||
sobj.save()
|
||||
sobj.unlock()
|
||||
del node_info['data']
|
||||
|
||||
def _process_data_for_discoverd(self, hw_items, node_info):
|
||||
matcher.match_spec(('memory', 'total', 'size', '$memory_mb'),
|
||||
hw_items, node_info)
|
||||
matcher.match_spec(('cpu', 'logical', 'number', '$cpus'),
|
||||
hw_items, node_info)
|
||||
matcher.match_spec(('system', 'kernel', 'arch', '$cpu_arch'),
|
||||
hw_items, node_info)
|
||||
matcher.match_spec(('disk', '$disk', 'size', '$local_gb'),
|
||||
hw_items, node_info)
|
||||
matcher.match_spec(('ipmi', 'lan', 'ip-address', '$ipmi_address'),
|
||||
hw_items, node_info)
|
||||
node_info['interfaces'] = {}
|
||||
while True:
|
||||
info = {'ipv4': 'none'}
|
||||
if not matcher.match_spec(('network', '$iface', 'serial', '$mac'),
|
||||
hw_items, info):
|
||||
break
|
||||
matcher.match_spec(('network', info['iface'], 'ipv4', '$ipv4'),
|
||||
hw_items, info)
|
||||
node_info['interfaces'][info['iface']] = {'mac': info['mac'],
|
||||
'ip': info['ipv4']}
|
||||
|
||||
def before_update(self, node, ports, node_info):
|
||||
"""Store the hardware data from what has been discovered."""
|
||||
|
||||
if 'hardware' in node_info:
|
||||
return [
|
||||
{'op': 'add',
|
||||
'path': '/extra/configdrive_metadata',
|
||||
'value': {'hardware': node_info['hardware']}},
|
||||
{'op': 'add',
|
||||
'path': '/properties/capabilities',
|
||||
'value': 'profile:%s' % node_info['hardware']['profile']}
|
||||
], {}
|
|
@ -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.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from hardware import state
|
||||
import mock
|
||||
|
||||
from ironic_discoverd import conf
|
||||
from ironic_discoverd.plugins import edeploy
|
||||
from ironic_discoverd import utils
|
||||
|
||||
|
||||
def fake_load(obj, cfg_dir):
|
||||
obj._cfg_dir = cfg_dir
|
||||
obj._data = [('hw1', '*'), ]
|
||||
|
||||
|
||||
@mock.patch.object(state.State, 'load', fake_load)
|
||||
@mock.patch.object(state.State, '_load_specs',
|
||||
lambda o, n: [('network', '$iface', 'serial', '$mac'),
|
||||
('network', '$iface', 'ipv4', '$ipv4')])
|
||||
class TestEdeploy(unittest.TestCase):
|
||||
def setUp(self):
|
||||
conf.init_conf()
|
||||
conf.CONF.add_section('edeploy')
|
||||
basedir = os.path.dirname(os.path.abspath(__file__))
|
||||
conf.CONF.set('edeploy', 'configdir', os.path.join(basedir,
|
||||
'edeploy_conf'))
|
||||
|
||||
def test_hook(self):
|
||||
hook = edeploy.eDeployHook()
|
||||
node_info = {'data': [
|
||||
['network', 'eth0', 'serial', '99:99:99:99:99:99'],
|
||||
['network', 'eth0', 'ipv4', '192.168.100.12'],
|
||||
]}
|
||||
hook.before_processing(node_info)
|
||||
self.assertEqual('hw1', node_info['hardware']['profile'])
|
||||
self.assertEqual('eth0', node_info['hardware']['iface'])
|
||||
self.assertEqual('192.168.100.12', node_info['hardware']['ipv4'])
|
||||
self.assertEqual('99:99:99:99:99:99',
|
||||
node_info['interfaces']['eth0']['mac'])
|
||||
node_patches, _ = hook.before_update(None, None, node_info)
|
||||
self.assertEqual('/extra/configdrive_metadata',
|
||||
node_patches[0]['path'])
|
||||
self.assertEqual('hw1',
|
||||
node_patches[0]['value']['hardware']['profile'])
|
||||
self.assertEqual('/properties/capabilities',
|
||||
node_patches[1]['path'])
|
||||
self.assertEqual('profile:hw1',
|
||||
node_patches[1]['value'])
|
||||
|
||||
def test_hook_no_data(self):
|
||||
hook = edeploy.eDeployHook()
|
||||
node_info = {}
|
||||
self.assertRaises(utils.Error, hook.before_processing, node_info)
|
||||
|
||||
@mock.patch.object(edeploy, 'LOG')
|
||||
def test_hook_no_profile(self, mock_log):
|
||||
hook = edeploy.eDeployHook()
|
||||
node_info = {'data': []}
|
||||
hook.before_processing(node_info)
|
||||
self.assertTrue(mock_log.warning.called)
|
1
setup.py
1
setup.py
|
@ -31,6 +31,7 @@ setup(
|
|||
"validate_interfaces = ironic_discoverd.plugins.standard:ValidateInterfacesHook",
|
||||
"ramdisk_error = ironic_discoverd.plugins.standard:RamdiskErrorHook",
|
||||
"example = ironic_discoverd.plugins.example:ExampleProcessingHook",
|
||||
"edeploy = ironic_discoverd.plugins.edeploy:eDeployHook",
|
||||
],
|
||||
},
|
||||
classifiers = [
|
||||
|
|
|
@ -5,3 +5,4 @@ hacking
|
|||
mock
|
||||
|
||||
# plugin-specific
|
||||
hardware>=0.7,<0.99
|
||||
|
|
Loading…
Reference in New Issue