Merge "IloVirtualMediaAgent deploy driver"
This commit is contained in:
commit
21e12485d3
|
@ -20,6 +20,7 @@ from oslo.utils import importutils
|
|||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules.ilo import deploy
|
||||
from ironic.drivers.modules.ilo import power
|
||||
from ironic.drivers.modules import ipmitool
|
||||
|
@ -46,3 +47,26 @@ class IloVirtualMediaIscsiDriver(base.BaseDriver):
|
|||
self.console = ipmitool.IPMIShellinaboxConsole()
|
||||
self.management = ipmitool.IPMIManagement()
|
||||
self.vendor = deploy.VendorPassthru()
|
||||
|
||||
|
||||
class IloVirtualMediaAgentDriver(base.BaseDriver):
|
||||
"""IloDriver using IloClient interface.
|
||||
|
||||
This driver implements the `core` functionality using
|
||||
:class:ironic.drivers.modules.ilo.power.IloPower for power management
|
||||
and
|
||||
:class:ironic.drivers.modules.ilo.deploy.IloVirtualMediaAgentDriver for
|
||||
deploy.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
if not importutils.try_import('proliantutils'):
|
||||
raise exception.DriverLoadError(
|
||||
driver=self.__class__.__name__,
|
||||
reason=_("Unable to import proliantutils library"))
|
||||
|
||||
self.power = power.IloPower()
|
||||
self.deploy = deploy.IloVirtualMediaAgentDeploy()
|
||||
self.console = ipmitool.IPMIShellinaboxConsole()
|
||||
self.management = ipmitool.IPMIManagement()
|
||||
self.vendor = agent.AgentVendorInterface()
|
||||
|
|
|
@ -74,15 +74,38 @@ def _get_client():
|
|||
return client
|
||||
|
||||
|
||||
def _build_pxe_config_options(pxe_info):
|
||||
def build_agent_options():
|
||||
"""Build the options to be passed to the agent ramdisk.
|
||||
|
||||
:returns: a dictionary containing the parameters to be passed to
|
||||
agent ramdisk.
|
||||
"""
|
||||
ironic_api = (CONF.conductor.api_url or
|
||||
keystone.get_service_url()).rstrip('/')
|
||||
return {
|
||||
'ipa-api-url': ironic_api,
|
||||
}
|
||||
|
||||
|
||||
def _build_pxe_config_options(pxe_info):
|
||||
"""Builds the pxe config options for booting agent.
|
||||
|
||||
This method builds the config options to be replaced on
|
||||
the agent pxe config template.
|
||||
|
||||
:param pxe_info: A dict containing the 'deploy_kernel' and
|
||||
'deploy_ramdisk' for the agent pxe config template.
|
||||
:returns: a dict containing the options to be applied on
|
||||
the agent pxe config template.
|
||||
"""
|
||||
agent_config_opts = {
|
||||
'deployment_aki_path': pxe_info['deploy_kernel'][1],
|
||||
'deployment_ari_path': pxe_info['deploy_ramdisk'][1],
|
||||
'pxe_append_params': CONF.agent.agent_pxe_append_params,
|
||||
'ipa_api_url': ironic_api,
|
||||
}
|
||||
agent_opts = build_agent_options()
|
||||
agent_config_opts.update(agent_opts)
|
||||
return agent_config_opts
|
||||
|
||||
|
||||
def _get_tftp_image_info(node):
|
||||
|
@ -162,8 +185,13 @@ def _cache_tftp_images(ctx, node, pxe_info):
|
|||
_fetch_images(ctx, AgentTFTPImageCache(), pxe_info.values())
|
||||
|
||||
|
||||
def _build_instance_info_for_deploy(task):
|
||||
"""Build instance_info necessary for deploying to a node."""
|
||||
def build_instance_info_for_deploy(task):
|
||||
"""Build instance_info necessary for deploying to a node.
|
||||
|
||||
:param task: a TaskManager object containing the node
|
||||
:returns: a dictionary containing the properties to be updated
|
||||
in instance_info
|
||||
"""
|
||||
node = task.node
|
||||
instance_info = node.instance_info
|
||||
|
||||
|
@ -248,7 +276,7 @@ class AgentDeploy(base.DeployInterface):
|
|||
CONF.agent.agent_pxe_config_template)
|
||||
_cache_tftp_images(task.context, node, pxe_info)
|
||||
|
||||
node.instance_info = _build_instance_info_for_deploy(task)
|
||||
node.instance_info = build_instance_info_for_deploy(task)
|
||||
node.save(task.context)
|
||||
|
||||
def clean_up(self, task):
|
||||
|
|
|
@ -2,4 +2,4 @@ default deploy
|
|||
|
||||
label deploy
|
||||
kernel {{ pxe_options.deployment_aki_path }}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} text {{ pxe_options.pxe_append_params }} {% if pxe_options.ipa_api_url %}ipa-api-url={{ pxe_options.ipa_api_url }}{% endif %} {% if pxe_options.ipa_advertise_host %}ipa-advertise-host={{ pxe_options.ipa_advertise_host }}{% endif %}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} text {{ pxe_options.pxe_append_params }} {% if pxe_options.ipa-api-url %}ipa-api-url={{ pxe_options.ipa-api-url }}{% endif %} {% if pxe_options.ipa-advertise-host %}ipa-advertise-host={{ pxe_options.ipa-advertise-host }}{% endif %}
|
||||
|
|
|
@ -28,6 +28,7 @@ from ironic.common import swift
|
|||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
|
@ -306,6 +307,81 @@ class IloVirtualMediaIscsiDeploy(base.DeployInterface):
|
|||
pass
|
||||
|
||||
|
||||
class IloVirtualMediaAgentDeploy(base.DeployInterface):
|
||||
"""Interface for deploy-related actions."""
|
||||
|
||||
def get_properties(self):
|
||||
"""Return the properties of the interface.
|
||||
|
||||
:returns: dictionary of <property name>:<property description> entries.
|
||||
"""
|
||||
return COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task):
|
||||
"""Validate the driver-specific Node deployment info.
|
||||
|
||||
:param task: a TaskManager instance
|
||||
:raises: MissingParameterValue if some parameters are missing.
|
||||
"""
|
||||
_parse_driver_info(task.node)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def deploy(self, task):
|
||||
"""Perform a deployment to a node.
|
||||
|
||||
Prepares the options for the agent ramdisk and sets the node to boot
|
||||
from virtual media cdrom.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:returns: states.DEPLOYWAIT
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy
|
||||
image.
|
||||
:raises: IloOperationError, if some operation on iLO fails.
|
||||
"""
|
||||
deploy_ramdisk_opts = agent.build_agent_options()
|
||||
deploy_iso_uuid = task.node.driver_info['ilo_deploy_iso']
|
||||
deploy_iso = 'glance:' + deploy_iso_uuid
|
||||
_reboot_into(task, deploy_iso, deploy_ramdisk_opts)
|
||||
|
||||
return states.DEPLOYWAIT
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def tear_down(self, task):
|
||||
"""Tear down a previous deployment on the task's node.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:returns: states.DELETED
|
||||
"""
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
return states.DELETED
|
||||
|
||||
def prepare(self, task):
|
||||
"""Prepare the deployment environment for this node.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
"""
|
||||
node = task.node
|
||||
node.instance_info = agent.build_instance_info_for_deploy(task)
|
||||
node.save(task.context)
|
||||
|
||||
def clean_up(self, task):
|
||||
"""Clean up the deployment environment for this node.
|
||||
|
||||
Ejects the attached virtual media from the iLO and also removes
|
||||
the floppy image from Swift, if it exists.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
"""
|
||||
ilo_common.cleanup_vmedia_boot(task)
|
||||
|
||||
def take_over(self, task):
|
||||
"""Take over management of this node from a dead conductor.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VendorPassthru(base.VendorInterface):
|
||||
"""Vendor-specific interfaces for iLO deploy drivers."""
|
||||
|
||||
|
|
|
@ -2269,6 +2269,16 @@ class ManagerTestProperties(tests_db_base.DbTestCase):
|
|||
'ipmi_target_address', 'ipmi_local_address']
|
||||
self._check_driver_properties("iscsi_ilo", expected)
|
||||
|
||||
def test_driver_properties_agent_ilo(self):
|
||||
expected = ['ilo_address', 'ilo_username', 'ilo_password',
|
||||
'client_port', 'client_timeout', 'ilo_deploy_iso',
|
||||
'ipmi_address', 'ipmi_terminal_port',
|
||||
'ipmi_password', 'ipmi_priv_level',
|
||||
'ipmi_username', 'ipmi_bridging', 'ipmi_transit_channel',
|
||||
'ipmi_transit_address', 'ipmi_target_channel',
|
||||
'ipmi_target_address', 'ipmi_local_address']
|
||||
self._check_driver_properties("agent_ilo", expected)
|
||||
|
||||
def test_driver_properties_fail(self):
|
||||
mgr_utils.mock_the_extension_manager()
|
||||
self.driver = driver_factory.get_driver("fake")
|
||||
|
|
|
@ -2,4 +2,4 @@ default deploy
|
|||
|
||||
label deploy
|
||||
kernel {{ pxe_options.deployment_aki_path }}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} root=squashfs: {% if pxe_options.pxe_append_params %}{{ pxe_options.pxe_append_params }}{% endif %} state=tmpfs: ipa-api-url={{ pxe_options.ipa_api_url }} {% if pxe_options.ipa_advertise_host %}ipa-advertise-host={{ pxe_options.ipa_advertise_host }}{% endif %}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} text root=squashfs: {% if pxe_options.pxe_append_params %}{{ pxe_options.pxe_append_params }}{% endif %} state=tmpfs: ipa-api-url={{ pxe_options.ipa-api-url }} {% if pxe_options.ipa-advertise-host %}ipa-advertise-host={{ pxe_options.ipa-advertise-host }}{% endif %}
|
||||
|
|
|
@ -27,6 +27,7 @@ from ironic.common import utils
|
|||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
from ironic.drivers.modules.ilo import deploy as ilo_deploy
|
||||
|
@ -249,6 +250,59 @@ class IloVirtualMediaIscsiDeployTestCase(base.TestCase):
|
|||
clean_up_boot_mock.assert_called_once_with(task.node)
|
||||
|
||||
|
||||
class IloVirtualMediaAgentDeployTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IloVirtualMediaAgentDeployTestCase, self).setUp()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.context = context.get_admin_context()
|
||||
mgr_utils.mock_the_extension_manager(driver="agent_ilo")
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='agent_ilo', driver_info=INFO_DICT)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_parse_driver_info')
|
||||
def test_validate(self, parse_driver_info_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
parse_driver_info_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_reboot_into')
|
||||
@mock.patch.object(agent, 'build_agent_options')
|
||||
def test_deploy(self, build_options_mock, reboot_into_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
deploy_opts = {'a': 'b'}
|
||||
build_options_mock.return_value = deploy_opts
|
||||
task.node.driver_info['ilo_deploy_iso'] = 'deploy-iso-uuid'
|
||||
|
||||
returned_state = task.driver.deploy.deploy(task)
|
||||
|
||||
build_options_mock.assert_called_once_with()
|
||||
reboot_into_mock.assert_called_once_with(task,
|
||||
'glance:deploy-iso-uuid',
|
||||
deploy_opts)
|
||||
self.assertEqual(states.DEPLOYWAIT, returned_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
def test_tear_down(self, node_power_action_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = task.driver.deploy.tear_down(task)
|
||||
node_power_action_mock.assert_called_once_with(task,
|
||||
states.POWER_OFF)
|
||||
self.assertEqual(states.DELETED, returned_state)
|
||||
|
||||
@mock.patch.object(agent, 'build_instance_info_for_deploy')
|
||||
def test_prepare(self, build_instance_info_mock):
|
||||
deploy_opts = {'a': 'b'}
|
||||
build_instance_info_mock.return_value = deploy_opts
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
self.assertEqual(deploy_opts, task.node.instance_info)
|
||||
|
||||
|
||||
class VendorPassthruTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -17,6 +17,7 @@ from oslo.config import cfg
|
|||
|
||||
from ironic.common import dhcp_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common import keystone
|
||||
from ironic.common import pxe_utils
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
|
@ -35,6 +36,24 @@ DRIVER_INFO = db_utils.get_test_agent_driver_info()
|
|||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestAgentMethods(db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestAgentMethods, self).setUp()
|
||||
|
||||
def test_build_agent_options_conf(self):
|
||||
self.config(api_url='api-url', group='conductor')
|
||||
options = agent.build_agent_options()
|
||||
self.assertEqual('api-url', options['ipa-api-url'])
|
||||
|
||||
@mock.patch.object(keystone, 'get_service_url')
|
||||
def test_build_agent_options_keystone(self, get_url_mock):
|
||||
|
||||
self.config(api_url=None, group='conductor')
|
||||
get_url_mock.return_value = 'api-url'
|
||||
options = agent.build_agent_options()
|
||||
self.assertEqual('api-url', options['ipa-api-url'])
|
||||
|
||||
|
||||
class TestAgentDeploy(db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestAgentDeploy, self).setUp()
|
||||
|
|
|
@ -35,6 +35,7 @@ ironic.dhcp =
|
|||
none = ironic.dhcp.none:NoneDHCPApi
|
||||
|
||||
ironic.drivers =
|
||||
agent_ilo = ironic.drivers.ilo:IloVirtualMediaAgentDriver
|
||||
agent_ipmitool = ironic.drivers.agent:AgentAndIPMIToolDriver
|
||||
agent_pyghmi = ironic.drivers.agent:AgentAndIPMINativeDriver
|
||||
agent_ssh = ironic.drivers.agent:AgentAndSSHDriver
|
||||
|
|
Loading…
Reference in New Issue