Source code for ironic.drivers.modules.autodetect
# 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 oslo_log import log as logging
from ironic.common import driver_factory
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import metrics_utils
from ironic.conf import CONF
from ironic.drivers import base
LOG = logging.getLogger(__name__)
METRICS = metrics_utils.get_metrics_logger(__name__)
[docs]
class AutodetectDeploy(base.DeployInterface):
"""Deploy interface that auto-detects the appropriate deployment method.
"""
def __init__(self):
super(AutodetectDeploy, self).__init__()
# Validate that all autodetect interfaces are enabled
for interface_name in CONF.autodetect_deploy_interfaces:
self._validate_autodetect_interface(interface_name)
def _validate_autodetect_interface(self, interface_name):
"""Validate that the autodetect interface is enabled.
:param interface_name: Name of the deploy interface to validate.
:raises: InvalidParameterValue if the interface is not enabled.
"""
enabled_interfaces = CONF.enabled_deploy_interfaces
if interface_name not in enabled_interfaces:
raise exception.InvalidParameterValue(
_("Deploy interface '%(interface)s' is configured in "
"autodetect_deploy_interfaces but is not in "
"enabled_deploy_interfaces. Please add '%(interface)s' "
"to enabled_deploy_interfaces or remove it from "
"autodetect_deploy_interfaces.")
% {'interface': interface_name})
[docs]
def get_properties(self):
"""Return the properties of the interface.
:returns: dictionary of <property name>:<property description> entries.
"""
return {}
[docs]
@METRICS.timer('AutodetectDeploy.validate')
def validate(self, task):
"""Validate the driver-specific Node deployment info.
This method creates the deploy interface that would be switched to
and calls its validate() method.
:param task: A TaskManager instance containing the node to act on.
:raises: MissingParameterValue if required parameters are missing.
"""
switchable = self._create_switchable_interface(task)
interface, interface_name, interface_supports = switchable
return interface.validate(task)
[docs]
@METRICS.timer('AutodetectDeploy.deploy')
@base.deploy_step(priority=100)
def deploy(self, task):
"""Perform a deployment to the task's node.
This method should not be called directly as the autodetect interface
is expected to switch to a concrete interface during
switch_interface(). If this is called, it means the interface switch
did not happen.
:param task: A TaskManager instance containing the node to act on.
:raises: InstanceDeployFailure if deployment fails.
"""
raise exception.InstanceDeployFailure(
_("Autodetect deploy interface did not switch to a concrete "
"interface during switch_interface(). This indicates a bug or "
"misconfiguration."))
[docs]
@METRICS.timer('AutodetectDeploy.tear_down')
def tear_down(self, task):
"""Tear down a previous deployment on the task's node.
:param task: A TaskManager instance containing the node to act on.
:returns: deploy state DELETED.
"""
# Autodetect deploy interface does not perform any actual deployment.
# This is handled by AgentBaseMixin.tear_down() for actual deployments
pass
[docs]
@METRICS.timer('AutodetectDeploy.prepare')
def prepare(self, task):
"""Prepare the deployment environment for the task's node.
"""
# Autodetect deploy interface does not perform any actual deployment.
# This is handled by AgentBaseMixin.prepare() for actual deployments
raise exception.InstanceDeployFailure(
_("Autodetect deploy interface did not switch to a concrete "
"interface during switch_interface(). This indicates a bug or "
"misconfiguration."))
[docs]
@METRICS.timer('AutodetectDeploy.clean_up')
def clean_up(self, task):
"""Clean up the deployment environment for the task's node.
:param task: A TaskManager instance containing the node to act on.
"""
pass
[docs]
@METRICS.timer('AutodetectDeploy.take_over')
def take_over(self, task):
"""Take over management of this task's node from a dead conductor.
:param task: A TaskManager instance containing the node to act on.
"""
pass
def _create_switchable_interface(self, task):
"""Detect and create the deploy interface to switch to.
:param task: A TaskManager instance containing the node to act on.
:raises: InvalidParameterValue if the interface is not enabled.
:returns: A tuple of (interface instance, interface name,
supports deploy).
"""
node = task.node
hw_type = driver_factory.get_hardware_type(node.driver)
interface = None
interface_name = None
interface_supports = False
for interface_name in CONF.autodetect_deploy_interfaces:
self._validate_autodetect_interface(interface_name)
# Get the new deploy interface instance from the factory
interface = driver_factory.get_interface(
hw_type, 'deploy', interface_name)
interface_supports = interface.supports_deploy(task)
if interface_supports:
break
if not interface:
raise exception.InvalidParameterValue(
_("No valid deploy interfaces found in "
"autodetect_deploy_interfaces configuration."))
return interface, interface_name, interface_supports
[docs]
@METRICS.timer('AutodetectDeploy.switch_interface')
def switch_interface(self, task):
"""Switch the interface to use for deployment.
This calls supports_deploy() methods of deploy interfaces
configured in the 'autodetect_deploy_interfaces' option, in order,
to determine which interface is supported for the current node/image.
The first interface that returns True from supports_deploy() is chosen.
If no interfaces are detected as supported, the last interface in the
list is chosen as the fallback.
:raises: InvalidParameterValue if the interface is not enabled.
:param task: A TaskManager instance containing the node to act on.
"""
switchable = self._create_switchable_interface(task)
interface, interface_name, interface_supports = switchable
if not interface_supports:
LOG.warning("No deploy interfaces in autodetect_deploy_interfaces "
"are supported for this node/image. "
"Using last interface: %s", interface_name)
LOG.info("autodetect switching to deploy interface: %s",
interface_name)
node = task.node
# Save the original deploy interface to restore later
node.set_driver_internal_info(
'original_deploy_interface',
task.node.deploy_interface)
# Update the node's deploy interface name
node.deploy_interface = interface_name
# Replace the deploy interface on the driver
task.driver.deploy = interface
node.save()