Source code for ironic.drivers.modules.inspector.agent

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

In-band inspection implementation.

from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils

from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import states
from ironic.conductor import utils as cond_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules import inspect_utils
from ironic.drivers.modules.inspector.hooks import base as hooks_base
from ironic.drivers.modules.inspector import interface as common
from ironic.drivers import utils as driver_utils

LOG = logging.getLogger(__name__)


[docs] class AgentInspect(common.Common): """In-band inspection.""" default_require_managed_boot = True def __init__(self): super().__init__() self.hooks = hooks_base.validate_inspection_hooks() def _start_managed_inspection(self, task): """Start inspection managed by ironic.""" ep = deploy_utils.get_ironic_api_url().rstrip('/') if ep.endswith('/v1'): ep = f'{ep}/continue_inspection' else: ep = f'{ep}/v1/continue_inspection' common.prepare_managed_inspection(task, ep) cond_utils.node_power_action(task, states.POWER_ON) def _start_unmanaged_inspection(self, task): """Start unmanaged inspection.""" try: cond_utils.node_power_action(task, states.POWER_OFF) # Only network boot is supported for unmanaged inspection. cond_utils.node_set_boot_device(task, boot_devices.PXE, persistent=False) cond_utils.node_power_action(task, states.POWER_ON) except Exception as exc: LOG.exception('Unable to start unmanaged inspection for node ' '%(uuid)s: %(err)s', {'uuid': task.node.uuid, 'err': exc}) error = _('unable to start inspection: %s') % exc common.inspection_error_handler(task, error, raise_exc=True, clean_up=False)
[docs] def abort(self, task): """Abort hardware inspection. :param task: a task from TaskManager. """ error = _("By request, the inspection operation has been aborted") inspect_utils.clear_lookup_addresses(task.node) common.inspection_error_handler(task, error, raise_exc=False, clean_up=True)
[docs] def continue_inspection(self, task, inventory, plugin_data): """Continue in-band hardware inspection. :param task: a task from TaskManager. :param inventory: hardware inventory from the node. :param plugin_data: optional plugin-specific data. """ # Run the inspection hooks run_inspection_hooks(task, inventory, plugin_data, self.hooks) common.clean_up(task, finish=False)
def _store_logs(plugin_data, node): logs = plugin_data.get('logs') if not logs: LOG.warning('No logs were passed by the ramdisk for node %(node)s.', {'node': node.uuid}) return try: driver_utils.store_ramdisk_logs(node, logs, label='inspect') except exception: LOG.exception('Could not store the ramdisk logs for node %(node)s. ', {'node': node.uuid})
[docs] def run_inspection_hooks(task, inventory, plugin_data, hooks): """Process data from the ramdisk using inspection hooks.""" _run_preprocess_hooks(task, inventory, plugin_data, hooks) try: _run_post_hooks(task, inventory, plugin_data, hooks) _power_off_node(task) except exception.HardwareInspectionFailure: with excutils.save_and_reraise_exception(): _store_logs(plugin_data, task.node) except Exception as exc: LOG.exception('Unexpected exception while running inspection hooks for' ' node %(node)s', {'node': task.node.uuid}) msg = _('Unexpected exception %(exc_class)s during processing for ' 'node: %(node)s. Error: %(error)s' % {'exc_class': exc.__class__.__name__, 'node': task.node.uuid, 'error': exc}) _store_logs(plugin_data, task.node) raise exception.HardwareInspectionFailure(error=msg) if CONF.agent.deploy_logs_collect == 'always': _store_logs(plugin_data, task.node)
def _run_preprocess_hooks(task, inventory, plugin_data, hooks): failures = [] for hook in hooks: LOG.debug('Running preprocess inspection hook: %(hook)s for node: ' '%(node)s', {'hook':, 'node': task.node.uuid}) # NOTE(dtantsur): catch exceptions, so that we have changes to update # node inspection status with after look up try: hook.obj.preprocess(task, inventory, plugin_data) except exception.HardwareInspectionFailure as exc: LOG.error('Preprocess hook: %(hook)s failed for node %(node)s ' 'with error: %(error)s', {'hook':, 'node': task.node.uuid, 'error': exc}) failures.append('Error in preprocess hook %(hook)s: %(error)s', {'hook':, 'error': exc}) except Exception as exc: LOG.exception('Preprocess hook: %(hook)s failed for node %(node)s ' 'with error: %(error)s', {'hook':, 'node': task.node.uuid, 'error': exc}) failures.append(_('Unexpected exception %(exc_class)s during ' 'preprocess hook %(hook)s: %(error)s' % {'exc_class': exc.__class__.__name__, 'hook':, 'error': exc})) if failures: msg = _('The following failures happened while running preprocess ' 'hooks for node %(node)s:\n%(failures)s', {'node': task.node.uuid, 'failures': '\n'.join(failures)}) _store_logs(plugin_data, task.node) raise exception.HardwareInspectionFailure(error=msg) # TODO(masghar): Store unprocessed inspection data def _run_post_hooks(task, inventory, plugin_data, hooks): for hook in hooks: LOG.debug('Running inspection hook %(hook)s for node %(node)s', {'hook':, 'node': task.node.uuid}) hook.obj.__call__(task, inventory, plugin_data) def _power_off_node(task): power_off = CONF.inspector.power_off if not power_off: return node = task.node LOG.debug('Forcing power off of node %(node)s', {'node': node.uuid}) try: cond_utils.node_power_action(task, states.POWER_OFF) except Exception as exc: msg = _("Failed to power off node %(node)s after inspection. Check " "its power management configuration. Error: %(error)s" % {'node': node.uuid, "error": exc}) raise exception.HardwareInspectionFailure(msg)'Node %(node)s powered off', {'node': node.uuid})