Merge "Add a noop deploy interface"

This commit is contained in:
Zuul
2025-11-18 17:21:20 +00:00
committed by Gerrit Code Review
5 changed files with 131 additions and 0 deletions
+48
View File
@@ -303,3 +303,51 @@ Limitations
``ironic-conductor`` host is not available. If you need the contaitainer
content localized to the conductor, consider utilizing your own container
registry.
.. _noop-deploy:
Noop deploy
===========
The ``noop`` deploy interface is a minimal deployment mechanism that performs
no actual deployment operations. It is designed for scenarios where operators
want to allocate nodes and mark them as ``active`` in Ironic without deploying
an operating system.
This is useful when nodes are being managed by external systems but tracked in
Ironic's inventory.
You can specify this deploy interface when creating or updating a node::
baremetal node create --driver manual-management --deploy-interface noop
baremetal node set <NODE> --deploy-interface noop
Usage with Nova
---------------
For deployments using Ironic in conjunction with Nova, the ``noop`` deploy
interface provides an alternative approach to adding production nodes that are
already running workloads. Unlike the :doc:`node adoption </admin/adoption>`
feature, which uses the ``adopt`` verb and is not suitable for Nova
deployments, the ``noop`` deploy interface allows you to use the normal
``deploy`` verb instead.
With the ``noop`` deploy interface, nodes can be moved through the normal
deployment workflow (from ``manageable`` to ``available`` to ``deploying`` to
``active``) without any actual deployment operations taking place. This enables
Nova to properly track the instances while Ironic skips all conductor-side
deployment actions.
.. note::
The ``noop`` deploy interface completely bypasses cleaning operations.
Even if a user cannot update ``Node.automated_clean``, as long as they set
``Node.instance_info[deploy_interface]`` to ``noop``, cleaning will be
skipped.
Configuration
-------------
Enable the interface in ``ironic.conf``::
[DEFAULT]
enabled_deploy_interfaces = direct,ramdisk,noop
+30
View File
@@ -20,6 +20,7 @@ exceptions for user-accessible actions.
"""
from ironic.common import exception
from ironic.common import states
from ironic.drivers import base
@@ -91,3 +92,32 @@ class NoFirmware(FailMixin, base.FirmwareInterface):
def cache_firmware_components(self, task):
pass
class NoDeploy(base.DeployInterface):
"""Deploy interface that does nothing and succeeds."""
def get_properties(self):
return {}
def validate(self, task):
# Intentionally accept any node config for noop deploy.
pass
@base.deploy_step(priority=100)
def deploy(self, task):
# Synchronous success (mirrors FakeDeploy behavior).
return None
def tear_down(self, task):
# Indicate the node is torn down.
return states.DELETED
def prepare(self, task):
pass
def clean_up(self, task):
pass
def take_over(self, task):
pass
@@ -73,3 +73,45 @@ class NoInterfacesTestCase(base.TestCase):
self.assertEqual({}, inst.get_properties())
self.assertRaises(exception.UnsupportedDriverExtension,
inst.validate, self.task)
class NoDeployTestCase(base.TestCase):
def setUp(self):
super(NoDeployTestCase, self).setUp()
self.task = mock.Mock(node=mock.Mock(driver='fake', spec=['driver']),
spec=['node'])
self.deploy = noop.NoDeploy()
def test_get_properties(self):
self.assertEqual({}, self.deploy.get_properties())
def test_validate(self):
# Should not raise
self.assertIsNone(self.deploy.validate(self.task))
def test_deploy(self):
# Should return None for synchronous completion
self.assertIsNone(self.deploy.deploy(self.task))
def test_tear_down(self):
from ironic.common import states
# Should return DELETED state
self.assertEqual(states.DELETED, self.deploy.tear_down(self.task))
def test_prepare(self):
# Should not raise
self.assertIsNone(self.deploy.prepare(self.task))
def test_clean_up(self):
# Should not raise
self.assertIsNone(self.deploy.clean_up(self.task))
def test_take_over(self):
# Should not raise
self.assertIsNone(self.deploy.take_over(self.task))
def test_load_by_name(self):
inst = noop.NoDeploy()
self.assertIsInstance(inst, noop.NoDeploy)
self.assertEqual({}, inst.get_properties())
+1
View File
@@ -85,6 +85,7 @@ bootc = "ironic.drivers.modules.agent:BootcAgentDeploy"
custom-agent = "ironic.drivers.modules.agent:CustomAgentDeploy"
direct = "ironic.drivers.modules.agent:AgentDeploy"
fake = "ironic.drivers.modules.fake:FakeDeploy"
noop = "ironic.drivers.modules.noop:NoDeploy"
ramdisk = "ironic.drivers.modules.ramdisk:RamdiskDeploy"
[project.entry-points."ironic.hardware.interfaces.firmware"]
@@ -0,0 +1,10 @@
---
features:
- |
Adds a ``noop`` deploy interface that performs no actual deployment
operations. This allows operators to allocate nodes and mark them as
``active`` in Ironic without deploying an operating system. This provides
an alternative approach to node adoption for Nova deployments, where the
normal deploy verb can be used instead of the adopt verb.
It is also useful when nodes are managed by external systems but tracked
in Ironic's inventory.