diff --git a/ironic_python_agent/agent.py b/ironic_python_agent/agent.py index 5eb1e28dd..b2e2e38eb 100644 --- a/ironic_python_agent/agent.py +++ b/ironic_python_agent/agent.py @@ -29,7 +29,6 @@ from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log import pkg_resources -from stevedore import extension from ironic_python_agent.api import app from ironic_python_agent import config @@ -171,12 +170,7 @@ class IronicPythonAgent(base.ExecuteCommandMixin): if bool(cfg.CONF.keyfile) != bool(cfg.CONF.certfile): LOG.warning("Only one of 'keyfile' and 'certfile' options is " "defined in config file. Its value will be ignored.") - self.ext_mgr = extension.ExtensionManager( - namespace='ironic_python_agent.extensions', - invoke_on_load=True, - propagate_map_exceptions=True, - invoke_kwds={'agent': self}, - ) + self.ext_mgr = base.init_ext_manager(self) self.api_url = api_url if not self.api_url or self.api_url == 'mdns': try: diff --git a/ironic_python_agent/extensions/base.py b/ironic_python_agent/extensions/base.py index a4314098b..d2ea4337b 100644 --- a/ironic_python_agent/extensions/base.py +++ b/ironic_python_agent/extensions/base.py @@ -19,6 +19,7 @@ import threading from oslo_log import log from oslo_utils import uuidutils +from stevedore import extension from ironic_python_agent import encoding from ironic_python_agent import errors @@ -331,3 +332,25 @@ def sync_command(command_name, validator=None): return wrapper return sync_decorator + + +_EXT_MANAGER = None + + +def init_ext_manager(agent): + global _EXT_MANAGER + _EXT_MANAGER = extension.ExtensionManager( + namespace='ironic_python_agent.extensions', + invoke_on_load=True, + propagate_map_exceptions=True, + invoke_kwds={'agent': agent}, + ) + return _EXT_MANAGER + + +def get_extension(name): + if _EXT_MANAGER is None: + raise errors.ExtensionError('Extension manager is not initialized') + ext = _EXT_MANAGER[name].obj + ext.ext_mgr = _EXT_MANAGER + return ext diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index a5b31c737..fb1a21020 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -37,6 +37,7 @@ import yaml from ironic_python_agent import encoding from ironic_python_agent import errors +from ironic_python_agent.extensions import base as ext_base from ironic_python_agent import netutils from ironic_python_agent import raid_utils from ironic_python_agent import utils @@ -1566,6 +1567,15 @@ class GenericHardwareManager(HardwareManager): 'reboot_requested': False, 'argsinfo': RAID_APPLY_CONFIGURATION_ARGSINFO, }, + { + 'step': 'write_image', + # NOTE(dtantsur): this step has to be proxied via an + # out-of-band step with the same name, hence the priority here + # doesn't really matter. + 'priority': 0, + 'interface': 'deploy', + 'reboot_requested': False, + }, ] def apply_configuration(self, node, ports, raid_config, @@ -1980,6 +1990,25 @@ class GenericHardwareManager(HardwareManager): 'errors': '; '.join(raid_errors)} raise errors.SoftwareRAIDError(error) + def write_image(self, node, ports, image_info, configdrive=None): + """A deploy step to write an image. + + Downloads and writes an image to disk if necessary. Also writes a + configdrive to disk if the configdrive parameter is specified. + + :param node: A dictionary of the node object + :param ports: A list of dictionaries containing information + of ports for the node + :param image_info: Image information dictionary. + :param configdrive: A string containing the location of the config + drive as a URL OR the contents (as gzip/base64) + of the configdrive. Optional, defaults to None. + """ + ext = ext_base.get_extension('standby') + cmd = ext.prepare_image(image_info=image_info, configdrive=configdrive) + # The result is asynchronous, wait here. + cmd.join() + def _compare_extensions(ext1, ext2): mgr1 = ext1.obj diff --git a/ironic_python_agent/tests/unit/base.py b/ironic_python_agent/tests/unit/base.py index 033c39cb6..941969856 100644 --- a/ironic_python_agent/tests/unit/base.py +++ b/ironic_python_agent/tests/unit/base.py @@ -23,6 +23,7 @@ from oslo_config import cfg from oslo_config import fixture as config_fixture from oslotest import base as test_base +from ironic_python_agent.extensions import base as ext_base from ironic_python_agent import utils CONF = cfg.CONF @@ -58,6 +59,8 @@ class IronicAgentTest(test_base.BaseTestCase): self.patch(subprocess, 'check_output', do_not_call) self.patch(utils, 'execute', do_not_call) + ext_base._EXT_MANAGER = None + def _set_config(self): self.cfg_fixture = self.useFixture(config_fixture.Config(CONF))