Add option `power_off_after_discovery`

Option controls whether to shut down node after the discovery.

Change-Id: I447a640a963eafac820c94db3dcd005268ae6bef
Implements: blueprint returning-to-ramdisk
This commit is contained in:
Dmitry Tantsur 2014-12-04 14:44:40 +01:00
parent 2f63aaba24
commit 9b1a164cfa
5 changed files with 43 additions and 19 deletions

View File

@ -189,6 +189,10 @@ HTTP API consist of 2 endpoints:
* 403 - node is not on discovery
* 404 - node cannot be found or multiple nodes found
Successful response body is a JSON dictionary with keys:
* ``node`` node as returned by Ironic
.. _bug #1391866: https://bugs.launchpad.net/ironic-discoverd/+bug/1391866
Change Log
@ -198,6 +202,8 @@ v1.0.0
~~~~~~
* ``/v1/continue`` is now sync and errors are returned.
* Option ``power_off_after_discovery`` controls whether to force power off
after the successful discovery, and is ``False`` by default.
* Discovery now times out by default.
* Add support for plugins that hook into data processing pipeline, see
`plugin-architecture blueprint`_ for details.

View File

@ -28,6 +28,8 @@
;timeout = 3600
; Amount of time in seconds, after which repeat clean up of timed out nodes.
;firewall_update_period = 60
; Whether to power off the ramdisk immediately after the successful discovery.
;power_off_after_discovery = false
; IP to listen on.
;listen_address = 0.0.0.0

View File

@ -28,6 +28,7 @@ DEFAULTS = {
'processing_hooks': 'scheduler',
'timeout': '3600',
'clean_up_period': '60',
'power_off_after_discovery': 'false',
}

View File

@ -97,9 +97,6 @@ def _process_node(ironic, node, node_info, valid_macs):
'database - skipping',
{'mac': mac, 'node': node.uuid})
patch = [{'op': 'add', 'path': '/extra/newly_discovered', 'value': 'true'},
{'op': 'remove', 'path': '/extra/on_discovery'}]
node_patches = []
port_patches = {}
for hook_ext in hooks:
@ -114,22 +111,28 @@ def _process_node(ironic, node, node_info, valid_macs):
port_patches = {mac: patch for (mac, patch) in port_patches.items()
if mac in ports and patch}
ironic.node.update(node.uuid, patch + node_patches)
ironic.node.update(node.uuid, node_patches)
for mac, patches in port_patches.items():
ironic.port.update(ports[mac].uuid, patches)
LOG.info('Node %s was updated with data from discovery process, forcing '
'power off', node.uuid)
LOG.info('Node %s was updated with data from discovery process', node.uuid)
firewall.update_filters(ironic)
try:
ironic.node.set_power_state(node.uuid, 'off')
except Exception as exc:
LOG.error('Failed to power off node %s, check it\'s power '
'management configuration:\n%s', node.uuid, exc)
raise utils.DiscoveryFailed('Failed to power off node %s' % node.uuid)
if conf.getboolean('discoverd', 'power_off_after_discovery'):
LOG.info('Forcing power off of node %s', node.uuid)
try:
ironic.node.set_power_state(node.uuid, 'off')
except Exception as exc:
LOG.error('Failed to power off node %s, check it\'s power '
'management configuration:\n%s', node.uuid, exc)
raise utils.DiscoveryFailed('Failed to power off node %s' %
node.uuid)
patch = [{'op': 'add', 'path': '/extra/newly_discovered', 'value': 'true'},
{'op': 'remove', 'path': '/extra/on_discovery'}]
ironic.node.update(node.uuid, patch)
def discover(uuids):

View File

@ -51,16 +51,19 @@ class BaseTest(unittest.TestCase):
@patch.object(utils, 'get_client', autospec=True)
class TestProcess(BaseTest):
def setUp(self):
super(TestProcess, self).setUp()
self.node = Mock(driver_info={'ipmi_address': '1.2.3.4'},
properties={'cpu_arch': 'i386', 'local_gb': 40},
uuid='uuid',
extra={'on_discovery': 'true'})
self.patch = [
{'op': 'add', 'path': '/extra/newly_discovered', 'value': 'true'},
{'op': 'remove', 'path': '/extra/on_discovery'},
self.patch1 = [
{'op': 'add', 'path': '/properties/cpus', 'value': '2'},
{'op': 'add', 'path': '/properties/memory_mb', 'value': '1024'},
]
self.patch2 = [
{'op': 'add', 'path': '/extra/newly_discovered', 'value': 'true'},
{'op': 'remove', 'path': '/extra/on_discovery'},
]
self.data = {
'ipmi_address': '1.2.3.4',
'cpus': 2,
@ -105,16 +108,17 @@ class TestProcess(BaseTest):
self.assertEqual(['11:22:33:44:55:66', '66:55:44:33:22:11'],
sorted(pop_mock.call_args[1]['mac']))
cli.node.update.assert_called_once_with(self.node.uuid,
self.patch + ['fake patch',
'fake patch 2'])
cli.node.update.assert_any_call(self.node.uuid,
self.patch1 + ['fake patch',
'fake patch 2'])
cli.node.update.assert_any_call(self.node.uuid, self.patch2)
self.assertEqual(2, cli.node.update.call_count)
cli.port.create.assert_any_call(node_uuid=self.node.uuid,
address='11:22:33:44:55:66')
cli.port.create.assert_any_call(node_uuid=self.node.uuid,
address='66:55:44:33:22:11')
self.assertEqual(2, cli.port.create.call_count)
filters_mock.assert_called_once_with(cli)
cli.node.set_power_state.assert_called_once_with(self.node.uuid, 'off')
cli.port.update.assert_called_once_with(self.port.uuid, ['port patch'])
pre_mock.assert_called_once_with(self.data)
@ -123,6 +127,14 @@ class TestProcess(BaseTest):
def test_ok(self, client_mock, pop_mock, filters_mock, pre_mock,
post_mock):
self._do_test(client_mock, pop_mock, filters_mock, pre_mock, post_mock)
self.assertFalse(client_mock.return_value.node.set_power_state.called)
def test_force_off(self, client_mock, pop_mock, filters_mock, pre_mock,
post_mock):
conf.CONF.set('discoverd', 'power_off_after_discovery', 'true')
self._do_test(client_mock, pop_mock, filters_mock, pre_mock, post_mock)
client_mock.return_value.node.set_power_state.assert_called_once_with(
self.node.uuid, 'off')
def test_deprecated_macs(self, client_mock, pop_mock, filters_mock,
pre_mock, post_mock):