summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRamakrishnan G <rameshg87@gmail.com>2014-08-08 23:18:59 +0530
committerJim Rollenhagen <jim@jimrollenhagen.com>2014-09-03 16:51:33 -0700
commit5894ff356ec788c8ed93e53f656dc7353148c444 (patch)
treee34c03b7f4c2f66e8a722df44012ea5a79f3854f
parent571579a00521097772438b2015dbd208e4f7320d (diff)
downloadironic-5894ff356ec788c8ed93e53f656dc7353148c444.tar.gz
IloVirtualMediaAgent deploy driver
This commit introduces a new deploy driver which uses iLO virtual media to boot up proliant baremetal nodes, and uses agent to deploy the baremetal nodes. This patch also changes agent pxe config template slightly, so that the names of agent ramdisk parameters generated in code and those expected by agent ramdisk are same. Change-Id: Ia5677dff294bc146b864bed180fbda939cf9bb38 Implements: blueprint ilo-virtualmedia-ipa
-rw-r--r--ironic/drivers/ilo.py24
-rw-r--r--ironic/drivers/modules/agent.py38
-rw-r--r--ironic/drivers/modules/agent_config.template2
-rw-r--r--ironic/drivers/modules/ilo/deploy.py76
-rw-r--r--ironic/tests/conductor/test_manager.py10
-rw-r--r--ironic/tests/drivers/agent_pxe_config.template2
-rw-r--r--ironic/tests/drivers/ilo/test_deploy.py54
-rw-r--r--ironic/tests/drivers/test_agent.py19
-rw-r--r--setup.cfg1
9 files changed, 219 insertions, 7 deletions
diff --git a/ironic/drivers/ilo.py b/ironic/drivers/ilo.py
index ff24c634f..3889bb147 100644
--- a/ironic/drivers/ilo.py
+++ b/ironic/drivers/ilo.py
@@ -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()
diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py
index 08cfdfd80..2ba5740b1 100644
--- a/ironic/drivers/modules/agent.py
+++ b/ironic/drivers/modules/agent.py
@@ -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):
diff --git a/ironic/drivers/modules/agent_config.template b/ironic/drivers/modules/agent_config.template
index 3f36d4e69..4f71f0dc7 100644
--- a/ironic/drivers/modules/agent_config.template
+++ b/ironic/drivers/modules/agent_config.template
@@ -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 %}
diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py
index 67a92d2db..d77a6594b 100644
--- a/ironic/drivers/modules/ilo/deploy.py
+++ b/ironic/drivers/modules/ilo/deploy.py
@@ -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."""
diff --git a/ironic/tests/conductor/test_manager.py b/ironic/tests/conductor/test_manager.py
index 1825e1c80..06025e53a 100644
--- a/ironic/tests/conductor/test_manager.py
+++ b/ironic/tests/conductor/test_manager.py
@@ -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")
diff --git a/ironic/tests/drivers/agent_pxe_config.template b/ironic/tests/drivers/agent_pxe_config.template
index ec68da5e0..25a2321ea 100644
--- a/ironic/tests/drivers/agent_pxe_config.template
+++ b/ironic/tests/drivers/agent_pxe_config.template
@@ -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 %}
diff --git a/ironic/tests/drivers/ilo/test_deploy.py b/ironic/tests/drivers/ilo/test_deploy.py
index d76697c40..5eb096471 100644
--- a/ironic/tests/drivers/ilo/test_deploy.py
+++ b/ironic/tests/drivers/ilo/test_deploy.py
@@ -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):
diff --git a/ironic/tests/drivers/test_agent.py b/ironic/tests/drivers/test_agent.py
index eb48eff0c..685d7ba18 100644
--- a/ironic/tests/drivers/test_agent.py
+++ b/ironic/tests/drivers/test_agent.py
@@ -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()
diff --git a/setup.cfg b/setup.cfg
index 8a1b8e77c..dcb3a33c9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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