ironic-python-agent/ironic_python_agent/extensions/service.py
Julia Kreger eb95273ffb Add get_service_steps logic to the agent
Initial code patches for service steps have merged in
ironic, and it is now time to add support into the
agent which allows service steps to be raised to
the service.

Updates the default hardware manager version to 1.2,
which has *rarely* been incremented due to oversight.

Change-Id: Iabd2c6c551389ec3c24e94b71245b1250345f7a7
2023-08-31 06:22:22 -07:00

104 lines
4.2 KiB
Python

# Copyright 2015 Rackspace, Inc.
#
# 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
from ironic_lib import exception as il_exc
from oslo_log import log
from ironic_python_agent import errors
from ironic_python_agent.extensions import base
from ironic_python_agent import hardware
LOG = log.getLogger()
class ServiceExtension(base.BaseAgentExtension):
@base.sync_command('get_service_steps')
def get_service_steps(self, node, ports):
"""Get the list of service steps supported for the node and ports
:param node: A dict representation of a node
:param ports: A dict representation of ports attached to node
:returns: A list of service steps with keys step, priority, and
reboot_requested
"""
LOG.debug('Getting service steps, called with node: %(node)s, '
'ports: %(ports)s', {'node': node, 'ports': ports})
hardware.cache_node(node)
# Results should be a dict, not a list
candidate_steps = hardware.dispatch_to_all_managers(
'get_service_steps', node, ports)
LOG.debug('Service steps before deduplication: %s', candidate_steps)
service_steps = hardware.deduplicate_steps(candidate_steps)
LOG.debug('Returning service steps: %s', service_steps)
return {
'service_steps': service_steps,
'hardware_manager_version': hardware.get_current_versions(),
}
@base.async_command('execute_service_step')
def execute_service_step(self, step, node, ports, service_version=None,
**kwargs):
"""Execute a service step.
:param step: A step with 'step', 'priority' and 'interface' keys
:param node: A dict representation of a node
:param ports: A dict representation of ports attached to node
:param service_version: The service version as returned by
hardware.get_current_versions() at the beginning
of the service operation.
:returns: a CommandResult object with command_result set to whatever
the step returns.
"""
# Ensure the agent is still the same version, or raise an exception
LOG.debug('Executing service step %s', step)
hardware.cache_node(node)
hardware.check_versions(service_version)
if 'step' not in step:
msg = 'Malformed service_step, no "step" key: %s' % step
LOG.error(msg)
raise ValueError(msg)
kwargs.update(step.get('args') or {})
try:
result = hardware.dispatch_to_managers(step['step'], node, ports,
**kwargs)
except (errors.RESTError, il_exc.IronicException):
LOG.exception('Error performing service step %s', step['step'])
raise
except Exception as e:
msg = ('Unexpected exception performing service step %(step)s. '
'%(cls)s: %(err)s' % {'step': step['step'],
'cls': e.__class__.__name__,
'err': e})
LOG.exception(msg)
raise errors.ServicingError(msg)
LOG.info('Service step completed: %(step)s, result: %(result)s',
{'step': step, 'result': result})
# Cast result tuples (like output of utils.execute) as lists, or
# API throws errors
if isinstance(result, tuple):
result = list(result)
# Return the step that was executed so we can dispatch
# to the appropriate Ironic interface
return {
'service_result': result,
'service_step': step
}