|
| 1 | +From f8a0de8a569ae7498bdb778ab3b903e790c5eb28 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Afonne-CID < [email protected]> |
| 3 | +Date: Tue, 21 Oct 2025 08:43:40 +0100 |
| 4 | +Subject: [PATCH] Add a `noop` deploy interface |
| 5 | + |
| 6 | +This change adds a NoDeploy class to allow for a truly minimal |
| 7 | +deployment interface with no-op implementations for all required |
| 8 | +methods. |
| 9 | + |
| 10 | +Closes-Bug: #2106550 |
| 11 | +Change-Id: Ic6faf34860efef9165ad868d57972cd5007eacd4 |
| 12 | +Signed-off-by: Afonne-CID < [email protected]> |
| 13 | +--- |
| 14 | + doc/source/admin/interfaces/deploy.rst | 45 +++++++++++++++++++ |
| 15 | + ironic/drivers/modules/noop.py | 30 +++++++++++++ |
| 16 | + .../tests/unit/drivers/modules/test_noop.py | 42 +++++++++++++++++ |
| 17 | + pyproject.toml | 1 + |
| 18 | + ...oop-deploy-interface-e0b1440f9de92dce.yaml | 8 ++++ |
| 19 | + 5 files changed, 126 insertions(+) |
| 20 | + create mode 100644 releasenotes/notes/add-noop-deploy-interface-e0b1440f9de92dce.yaml |
| 21 | + |
| 22 | +diff --git a/ironic/drivers/modules/noop.py b/ironic/drivers/modules/noop.py |
| 23 | +index 491e1db61..5a85ac277 100644 |
| 24 | +--- a/ironic/drivers/modules/noop.py |
| 25 | ++++ b/ironic/drivers/modules/noop.py |
| 26 | +@@ -20,6 +20,7 @@ exceptions for user-accessible actions. |
| 27 | + """ |
| 28 | + |
| 29 | + from ironic.common import exception |
| 30 | ++from ironic.common import states |
| 31 | + from ironic.drivers import base |
| 32 | + |
| 33 | + |
| 34 | +@@ -91,3 +92,32 @@ class NoFirmware(FailMixin, base.FirmwareInterface): |
| 35 | + |
| 36 | + def cache_firmware_components(self, task): |
| 37 | + pass |
| 38 | ++ |
| 39 | ++ |
| 40 | ++class NoDeploy(base.DeployInterface): |
| 41 | ++ """Deploy interface that does nothing and succeeds.""" |
| 42 | ++ |
| 43 | ++ def get_properties(self): |
| 44 | ++ return {} |
| 45 | ++ |
| 46 | ++ def validate(self, task): |
| 47 | ++ # Intentionally accept any node config for noop deploy. |
| 48 | ++ pass |
| 49 | ++ |
| 50 | ++ @base.deploy_step(priority=100) |
| 51 | ++ def deploy(self, task): |
| 52 | ++ # Synchronous success (mirrors FakeDeploy behavior). |
| 53 | ++ return None |
| 54 | ++ |
| 55 | ++ def tear_down(self, task): |
| 56 | ++ # Indicate the node is torn down. |
| 57 | ++ return states.DELETED |
| 58 | ++ |
| 59 | ++ def prepare(self, task): |
| 60 | ++ pass |
| 61 | ++ |
| 62 | ++ def clean_up(self, task): |
| 63 | ++ pass |
| 64 | ++ |
| 65 | ++ def take_over(self, task): |
| 66 | ++ pass |
| 67 | +diff --git a/ironic/tests/unit/drivers/modules/test_noop.py b/ironic/tests/unit/drivers/modules/test_noop.py |
| 68 | +index 692b5aa04..dcf0f67d1 100644 |
| 69 | +--- a/ironic/tests/unit/drivers/modules/test_noop.py |
| 70 | ++++ b/ironic/tests/unit/drivers/modules/test_noop.py |
| 71 | +@@ -73,3 +73,45 @@ class NoInterfacesTestCase(base.TestCase): |
| 72 | + self.assertEqual({}, inst.get_properties()) |
| 73 | + self.assertRaises(exception.UnsupportedDriverExtension, |
| 74 | + inst.validate, self.task) |
| 75 | ++ |
| 76 | ++ |
| 77 | ++class NoDeployTestCase(base.TestCase): |
| 78 | ++ |
| 79 | ++ def setUp(self): |
| 80 | ++ super(NoDeployTestCase, self).setUp() |
| 81 | ++ self.task = mock.Mock(node=mock.Mock(driver='fake', spec=['driver']), |
| 82 | ++ spec=['node']) |
| 83 | ++ self.deploy = noop.NoDeploy() |
| 84 | ++ |
| 85 | ++ def test_get_properties(self): |
| 86 | ++ self.assertEqual({}, self.deploy.get_properties()) |
| 87 | ++ |
| 88 | ++ def test_validate(self): |
| 89 | ++ # Should not raise |
| 90 | ++ self.assertIsNone(self.deploy.validate(self.task)) |
| 91 | ++ |
| 92 | ++ def test_deploy(self): |
| 93 | ++ # Should return None for synchronous completion |
| 94 | ++ self.assertIsNone(self.deploy.deploy(self.task)) |
| 95 | ++ |
| 96 | ++ def test_tear_down(self): |
| 97 | ++ from ironic.common import states |
| 98 | ++ # Should return DELETED state |
| 99 | ++ self.assertEqual(states.DELETED, self.deploy.tear_down(self.task)) |
| 100 | ++ |
| 101 | ++ def test_prepare(self): |
| 102 | ++ # Should not raise |
| 103 | ++ self.assertIsNone(self.deploy.prepare(self.task)) |
| 104 | ++ |
| 105 | ++ def test_clean_up(self): |
| 106 | ++ # Should not raise |
| 107 | ++ self.assertIsNone(self.deploy.clean_up(self.task)) |
| 108 | ++ |
| 109 | ++ def test_take_over(self): |
| 110 | ++ # Should not raise |
| 111 | ++ self.assertIsNone(self.deploy.take_over(self.task)) |
| 112 | ++ |
| 113 | ++ def test_load_by_name(self): |
| 114 | ++ inst = noop.NoDeploy() |
| 115 | ++ self.assertIsInstance(inst, noop.NoDeploy) |
| 116 | ++ self.assertEqual({}, inst.get_properties()) |
| 117 | +diff --git a/setup.cfg b/setup.cfg |
| 118 | +index 6e8a19236..d9c516a1c 100644 |
| 119 | +--- a/setup.cfg |
| 120 | ++++ b/setup.cfg |
| 121 | +@@ -102,6 +102,7 @@ ironic.hardware.interfaces.deploy = |
| 122 | + anaconda = ironic.drivers.modules.pxe:PXEAnacondaDeploy |
| 123 | + ansible = ironic.drivers.modules.ansible.deploy:AnsibleDeploy |
| 124 | + bootc = ironic.drivers.modules.agent:BootcAgentDeploy |
| 125 | + custom-agent = ironic.drivers.modules.agent:CustomAgentDeploy |
| 126 | + direct = ironic.drivers.modules.agent:AgentDeploy |
| 127 | + fake = ironic.drivers.modules.fake:FakeDeploy |
| 128 | ++ noop = ironic.drivers.modules.noop:NoDeploy |
| 129 | + ramdisk = ironic.drivers.modules.ramdisk:RamdiskDeploy |
| 130 | + |
| 131 | + ironic.hardware.interfaces.firmware = |
| 132 | +-- |
| 133 | +2.39.2 (Apple Git-143) |
0 commit comments