Ironic Config Intergration for IPA

Update container-based cleaning hardware manager to use ironic
conductor config.
Note:
- Moved conf variables from __init__ and evaluate_hardware_support
 as the config overwritten after those process
- Utilized getattr instead of making methods beforehand. The methods
 created don't stick for a new instance.
Partial-Bug: #2100556
Change-Id: I53d5a4f112fbed455d5574840611ef6ea2db3eae
This commit is contained in:
satoshi-sh
2025年02月14日 18:45:42 +00:00
parent 7efe3dfc04
commit c3e9266f92

View File

@@ -455,6 +455,9 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
# Update config with values from Ironic
config = content.get('config', {})
if config.get('agent_containers'):
for opt, val in config['agent_containers'].items():
cfg.CONF.set_override(opt, val, group='container')
if config.get('metrics'):
for opt, val in config.items():
setattr(cfg.CONF.metrics, opt, val)

View File

@@ -443,6 +443,13 @@ disk_part_opts = [
]
container_opts = [
cfg.BoolOpt('allow_arbitrary_containers',
default=False,
help='Allow arbitrary containers to be run'
'without restriction.'),
cfg.ListOpt('allowed_containers',
default=[],
help='List of allowed containers that can be run.'),
cfg.StrOpt('container_steps_file',
default='/etc/ironic-python-agent.d/mysteps.yaml',
help='Path to the YAML file containing container-based'
@@ -457,13 +464,9 @@ container_opts = [
cfg.ListOpt('run_options',
default=['--rm', '--network=host', '--tls-verify=false'],
help='Options to use when running containers.'),
cfg.BoolOpt('allow_arbitrary_containers',
default=False,
help='Allow arbitrary containers to be run'
'without restriction.'),
cfg.ListOpt('allowed_containers',
default=[],
help='List of allowed containers that can be run.'),
cfg.StrOpt('container_conf_file',
default='/etc/containers/containers.conf',
help='Path to the container configuration file in the IPA RAM')
]

View File

@@ -268,6 +268,15 @@ class HardwareManagerMethodNotFound(RESTError):
super(HardwareManagerMethodNotFound, self).__init__(details)
class HardwareManagerConfigurationError(RESTError):
"""Error raised when a hardware manager has invalid configuration."""
message = 'Hardware manager configuration error'
def __init__(self, details=None):
super(HardwareManagerConfigurationError, self).__init__(details)
class IncompatibleHardwareMethodError(RESTError):
"""Error raised when HardwareManager method incompatible with hardware."""

View File

@@ -3652,6 +3652,8 @@ def dispatch_to_managers(method, *args, **kwargs):
:param kwargs: keyword arguments to dispatched method
:returns: result of successful dispatch of method
:raises HardwareManagerConfigurationError: if a hardware manager is
misconfigured
:raises HardwareManagerMethodNotFound: if all managers failed the method
:raises HardwareManagerNotFound: if no valid hardware managers found
"""
@@ -3660,6 +3662,11 @@ def dispatch_to_managers(method, *args, **kwargs):
if getattr(manager, method, None):
try:
return getattr(manager, method)(*args, **kwargs)
except errors.HardwareManagerConfigurationError as e:
LOG.error('Configuration error in HardwareManager'
'%(manager)s: %(e)s',
{'manager': manager, 'e': e})
raise
except errors.IncompatibleHardwareMethodError:
LOG.debug('HardwareManager %(manager)s does not '
'support %(method)s',

View File

@@ -10,7 +10,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from ironic_python_agent import errors
from ironic_python_agent import hardware
from ironic_python_agent import utils
from oslo_config import cfg
@@ -27,24 +27,9 @@ class ContainerHardwareManager(hardware.HardwareManager):
HARDWARE_MANAGER_NAME = "ContainerHardwareManager"
HARDWARE_MANAGER_VERSION = "1"
ALLOW_ARBITARY_CONTAINERS = CONF.container.allow_arbitrary_containers
ALLOWED_CONTAINERS = CONF.container.allowed_containers
def __init__(self):
"""Dynamically create cleanup methods."""
self.STEPS = self._load_steps_from_yaml(
CONF.container.container_steps_file)
LOG.debug("Loaded steps: %s", self.STEPS)
for step in self.STEPS:
if not self.ALLOW_ARBITARY_CONTAINERS:
if step['image'] not in self.ALLOWED_CONTAINERS:
LOG.debug(
"%s is not registered as ALLOWED_CONTAINERS "
"in ironic-python-agent.conf", step['image']
)
continue
setattr(self, step["name"], self._create_cleanup_method(step))
self.STEPS = None
def _load_steps_from_yaml(self, file_path):
"""Load steps from YAML file."""
@@ -58,52 +43,86 @@ class ContainerHardwareManager(hardware.HardwareManager):
def evaluate_hardware_support(self):
"""Determine if container runner exists and return support level."""
try:
stdout, _ = utils.execute("which", CONF.container.runner)
if stdout.strip():
containers_runners = ["podman", "docker"]
for runner in containers_runners:
try:
stdout, _ = utils.execute("which", runner)
LOG.debug("Found %s, returning MAINLINE",
CONF.container.runner)
runner)
return hardware.HardwareSupport.MAINLINE
except Exception as e:
LOG.debug("Error checking container runner: %s", str(e))
except Exception as e:
LOG.debug("Error checking container runner: %s", e)
LOG.debug("No container runner found, returning NONE")
return hardware.HardwareSupport.NONE
def get_clean_steps(self, node, ports):
"""Dynamically generate cleaning steps."""
self.STEPS = self._load_steps_from_yaml(
CONF.container['container_steps_file'])
steps = []
for step in self.STEPS:
steps.append(
{
"step": step["name"],
"priority": step["priority"],
"interface": step['interface'],
"reboot_requested": step['reboot_requested'],
"abortable": step["abortable"],
}
)
try:
steps.append(
{
"step": step["name"],
"priority": step["priority"],
"interface": step['interface'],
"reboot_requested": step['reboot_requested'],
"abortable": step["abortable"],
}
)
except KeyError as e:
missing_key = str(e)
step_name = step.get("name", "unknown")
LOG.exception("Missing key '%s' in cleaning step: %s",
missing_key, step_name)
raise errors.HardwareManagerConfigurationError(
f"Missing required key '{missing_key}' in cleaning step:\
{step_name}"
)
return steps
def _create_cleanup_method(self, step):
"""Return a function that runs the container with the given image."""
def __getattr__(self, name):
ALLOW_ARBITRARY_CONTAINERS = CONF.container
['allow_arbitrary_containers']
ALLOWED_CONTAINERS = CONF.container['allowed_containers']
if self.STEPS is None:
self.STEPS = self._load_steps_from_yaml(
CONF.container['container_steps_file'])
def _cleanup(node, ports):
try:
utils.execute(
CONF.container.runner,
"pull",
*step.get("pull_options", CONF.container.pull_options),
step["image"],
)
utils.execute(
CONF.container.runner,
"run",
*step.get("run_options", CONF.container.run_options),
step["image"],
)
except Exception as e:
LOG.exception("Error during cleanup: %s", e)
raise
return True
for step in self.STEPS:
if step.get('name') == name:
if not ALLOW_ARBITRARY_CONTAINERS:
if step.get('image') not in ALLOWED_CONTAINERS:
LOG.debug(
"%s is not registered as ALLOWED_CONTAINERS "
"in ironic-python-agent.conf", step.get('image')
)
continue
return _cleanup
def run_container_steps(*args, **kwargs):
try:
utils.execute(
CONF.container.runner,
"pull",
*step.get("pull_options",
CONF.container.pull_options),
step.get("image"),
)
LOG.info("Container image '%s' pulled",
step.get("image"))
utils.execute(
CONF.container.runner,
"run",
*step.get("run_options",
CONF.container.run_options),
step.get("image"),
)
LOG.info("Container image '%s' completed",
step.get("image"))
except Exception as e:
LOG.exception("Error during cleanup: %s", e)
raise
return run_container_steps
raise AttributeError(
"%s object has no attribute %s", self.__class__.__name__, name)
Reference in New Issue
openstack/ironic-python-agent
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.

The note is not visible to the blocked user.