From f71c36ec10a7ec244de4839f5768699d5eaf022a Mon Sep 17 00:00:00 2001 From: Anusha Ramineni Date: Tue, 21 Oct 2014 22:02:01 +0530 Subject: [PATCH] iLO Management Interface This commit is to support management Interface for HP ProLiant Servers using iLO client python library. Change-Id: I2d909c682b0db6d6bc447c433e3448d31d3558a4 Implements: blueprint ilo-management-interface --- ironic/drivers/fake.py | 2 + ironic/drivers/ilo.py | 5 +- ironic/drivers/modules/ilo/common.py | 44 ++--- ironic/drivers/modules/ilo/deploy.py | 103 +---------- ironic/drivers/modules/ilo/management.py | 164 ++++++++++++++++ ironic/drivers/modules/ilo/power.py | 4 +- ironic/drivers/pxe.py | 6 +- ironic/tests/drivers/ilo/test_common.py | 31 ++-- ironic/tests/drivers/ilo/test_deploy.py | 53 ++---- ironic/tests/drivers/ilo/test_management.py | 195 ++++++++++++++++++++ ironic/tests/drivers/ilo/test_power.py | 7 +- 11 files changed, 433 insertions(+), 181 deletions(-) create mode 100644 ironic/drivers/modules/ilo/management.py create mode 100644 ironic/tests/drivers/ilo/test_management.py diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py index b9f8ab9f50..678db4f78f 100644 --- a/ironic/drivers/fake.py +++ b/ironic/drivers/fake.py @@ -27,6 +27,7 @@ from ironic.drivers.modules.drac import management as drac_mgmt from ironic.drivers.modules.drac import power as drac_power from ironic.drivers.modules import fake from ironic.drivers.modules import iboot +from ironic.drivers.modules.ilo import management as ilo_management from ironic.drivers.modules.ilo import power as ilo_power from ironic.drivers.modules import ipminative from ironic.drivers.modules import ipmitool @@ -143,6 +144,7 @@ class FakeIloDriver(base.BaseDriver): reason=_("Unable to import proliantutils library")) self.power = ilo_power.IloPower() self.deploy = fake.FakeDeploy() + self.management = ilo_management.IloManagement() class FakeDracDriver(base.BaseDriver): diff --git a/ironic/drivers/ilo.py b/ironic/drivers/ilo.py index ce845a638b..921adece17 100644 --- a/ironic/drivers/ilo.py +++ b/ironic/drivers/ilo.py @@ -22,6 +22,7 @@ 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 management from ironic.drivers.modules.ilo import power @@ -44,7 +45,7 @@ class IloVirtualMediaIscsiDriver(base.BaseDriver): self.power = power.IloPower() self.deploy = deploy.IloVirtualMediaIscsiDeploy() self.console = deploy.IloConsoleInterface() - self.management = deploy.IloManagement() + self.management = management.IloManagement() self.vendor = deploy.VendorPassthru() @@ -67,5 +68,5 @@ class IloVirtualMediaAgentDriver(base.BaseDriver): self.power = power.IloPower() self.deploy = deploy.IloVirtualMediaAgentDeploy() self.console = deploy.IloConsoleInterface() - self.management = deploy.IloManagement() + self.management = management.IloManagement() self.vendor = agent.AgentVendorInterface() diff --git a/ironic/drivers/modules/ilo/common.py b/ironic/drivers/modules/ilo/common.py index 9749f4cd43..f27cf52311 100644 --- a/ironic/drivers/modules/ilo/common.py +++ b/ironic/drivers/modules/ilo/common.py @@ -190,6 +190,26 @@ def get_ilo_license(node): return STANDARD_LICENSE +def update_ipmi_properties(task): + """Update ipmi properties to node driver_info + + :param task: a task from TaskManager. + """ + node = task.node + info = node.driver_info + + # updating ipmi credentials + info['ipmi_address'] = info.get('ilo_address') + info['ipmi_username'] = info.get('ilo_username') + info['ipmi_password'] = info.get('ilo_password') + + if 'console_port' in info: + info['ipmi_terminal_port'] = info['console_port'] + + # saving ipmi credentials to task object + task.node.driver_info = info + + def _get_floppy_image_name(node): """Returns the floppy image name for a given node. @@ -273,30 +293,6 @@ def attach_vmedia(node, device, url): LOG.info(_LI("Attached virtual media %s successfully."), device) -# TODO(rameshg87): This needs to be moved to iLO's management interface. -def set_boot_device(node, device, persistent=False): - """Sets the node to boot from a device for the next boot. - - :param node: an ironic node object. - :param device: the device to boot from - :raises: IloOperationError if setting boot device failed. - """ - ilo_object = get_ilo_object(node) - - try: - if not persistent: - ilo_object.set_one_time_boot(device) - else: - ilo_object.update_persistent_boot([device]) - except ilo_client.IloError as ilo_exception: - operation = _("Setting %s as boot device") % device - raise exception.IloOperationError(operation=operation, - error=ilo_exception) - - LOG.debug("Node %(uuid)s set to boot from %(device)s.", - {'uuid': node.uuid, 'device': device}) - - def set_boot_mode(node, boot_mode): """Sets the node to boot using boot_mode for the next boot. diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py index 27fc09f25a..5d0c631e29 100644 --- a/ironic/drivers/modules/ilo/deploy.py +++ b/ironic/drivers/modules/ilo/deploy.py @@ -19,6 +19,7 @@ import tempfile from oslo.config import cfg +from ironic.common import boot_devices from ironic.common import exception from ironic.common.i18n import _ from ironic.common.i18n import _LE @@ -53,29 +54,6 @@ CONF.import_opt('pxe_append_params', 'ironic.drivers.modules.iscsi_deploy', CONF.import_opt('swift_ilo_container', 'ironic.drivers.modules.ilo.common', group='ilo') -BOOT_DEVICE_MAPPING_TO_ILO = {'pxe': 'NETWORK', 'disk': 'HDD', - 'cdrom': 'CDROM', 'bios': 'BIOS', 'safe': 'SAFE'} - - -def _update_ipmi_properties(task): - """Update ipmi properties to node driver_info - - :param task: a task from TaskManager. - """ - node = task.node - info = node.driver_info - - # updating ipmi credentials - info['ipmi_address'] = info['ilo_address'] - info['ipmi_username'] = info['ilo_username'] - info['ipmi_password'] = info['ilo_password'] - - if 'console_port' in info: - info['ipmi_terminal_port'] = info['console_port'] - - # saving ipmi credentials to task object - task.node.driver_info = info - def _get_boot_iso_object_name(node): """Returns the floppy image name for a given node. @@ -250,7 +228,7 @@ def _reboot_into(task, iso, ramdisk_options): :raises: IloOperationError, if some operation on iLO failed. """ ilo_common.setup_vmedia_for_boot(task, iso, ramdisk_options) - ilo_common.set_boot_device(task.node, 'CDROM') + manager_utils.node_set_boot_device(task, boot_devices.CDROM) manager_utils.node_power_action(task, states.REBOOT) @@ -464,79 +442,10 @@ class IloPXEDeploy(pxe.PXEDeploy): :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ - ilo_common.set_boot_device(task.node, 'NETWORK', False) + manager_utils.node_set_boot_device(task, boot_devices.PXE) return super(IloPXEDeploy, self).deploy(task) -class IloManagement(ipmitool.IPMIManagement): - - # Currently adding support to set_boot_device through iLO. All other - # functionalities (get_sensors_data etc) will be used from IPMI. - - # TODO(ramineni):To support other functionalities also using iLO. - - def get_properties(self): - return ilo_common.REQUIRED_PROPERTIES - - def validate(self, task): - """Check that 'driver_info' contains ILO and IPMI credentials. - - Validates whether the 'driver_info' property of the supplied - task's node contains the required credentials information. - - :param task: a task from TaskManager. - :raises: InvalidParameterValue if required IPMI/iLO parameters - are missing. - :raises: MissingParameterValue if a required parameter is missing. - - """ - ilo_common.parse_driver_info(task.node) - _update_ipmi_properties(task) - super(IloManagement, self).validate(task) - - @task_manager.require_exclusive_lock - def set_boot_device(self, task, device, persistent=False): - """Set the boot device for the task's node. - - Set the boot device to use on next reboot of the node. - - :param task: a task from TaskManager. - :param device: the boot device, one of - :mod:`ironic.common.boot_devices`. - :param persistent: Boolean value. True if the boot device will - persist to all future boots, False if not. - Default: False. - :raises: InvalidParameterValue if an invalid boot device is specified - :raises: MissingParameterValue if required ilo credentials are missing. - :raises: IloOperationError, if unable to set the boot device. - - """ - try: - boot_device = BOOT_DEVICE_MAPPING_TO_ILO[device] - except KeyError: - raise exception.InvalidParameterValue(_( - "Invalid boot device %s specified.") % device) - - ilo_common.parse_driver_info(task.node) - ilo_common.set_boot_device(task.node, boot_device, persistent) - - def get_sensors_data(self, task): - """Get sensors data. - - :param task: a TaskManager instance. - :raises: FailedToGetSensorData when getting the sensor data fails. - :raises: FailedToParseSensorData when parsing sensor data fails. - :raises: InvalidParameterValue if required ipmi/iLO parameters - are missing. - :raises: MissingParameterValue if a required parameter is missing. - :returns: returns a dict of sensor data group by sensor type. - - """ - ilo_common.parse_driver_info(task.node) - _update_ipmi_properties(task) - super(IloManagement, self).get_sensors_data(task) - - class IloConsoleInterface(ipmitool.IPMIShellinaboxConsole): """A ConsoleInterface that uses ipmitool and shellinabox.""" @@ -559,7 +468,7 @@ class IloConsoleInterface(ipmitool.IPMIShellinaboxConsole): raise exception.MissingParameterValue(_( "Missing 'console_port' parameter in node's driver_info.")) - _update_ipmi_properties(task) + ilo_common.update_ipmi_properties(task) super(IloConsoleInterface, self).validate(task) @@ -567,7 +476,7 @@ class IloPXEVendorPassthru(pxe.VendorPassthru): @base.passthru(['POST'], method='pass_deploy_info') def _continue_deploy(self, task, **kwargs): - ilo_common.set_boot_device(task.node, 'NETWORK', True) + manager_utils.node_set_boot_device(task, boot_devices.PXE, True) super(IloPXEVendorPassthru, self)._continue_deploy(task, **kwargs) @@ -623,7 +532,7 @@ class VendorPassthru(base.VendorInterface): return ilo_common.setup_vmedia_for_boot(task, boot_iso) - ilo_common.set_boot_device(node, 'CDROM') + manager_utils.node_set_boot_device(task, boot_devices.CDROM) address = kwargs.get('address') deploy_utils.notify_deploy_complete(address) diff --git a/ironic/drivers/modules/ilo/management.py b/ironic/drivers/modules/ilo/management.py new file mode 100644 index 0000000000..33097e7ae2 --- /dev/null +++ b/ironic/drivers/modules/ilo/management.py @@ -0,0 +1,164 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# 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. +""" +iLO Management Interface +""" + +from oslo.utils import importutils + +from ironic.common import boot_devices +from ironic.common import exception +from ironic.common.i18n import _ +from ironic.conductor import task_manager +from ironic.drivers import base +from ironic.drivers.modules.ilo import common as ilo_common +from ironic.drivers.modules import ipmitool +from ironic.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + +ilo_client = importutils.try_import('proliantutils.ilo.ribcl') + +BOOT_DEVICE_MAPPING_TO_ILO = {boot_devices.PXE: 'NETWORK', + boot_devices.DISK: 'HDD', + boot_devices.CDROM: 'CDROM' + } +BOOT_DEVICE_ILO_TO_GENERIC = {v: k + for k, v in BOOT_DEVICE_MAPPING_TO_ILO.items()} + + +class IloManagement(base.ManagementInterface): + + def get_properties(self): + return ilo_common.REQUIRED_PROPERTIES + + def validate(self, task): + """Check that 'driver_info' contains required ILO credentials. + + Validates whether the 'driver_info' property of the supplied + task's node contains the required credentials information. + + :param task: a task from TaskManager. + :raises: InvalidParameterValue if required iLO parameters + are not valid. + :raises: MissingParameterValue if a required parameter is missing. + + """ + ilo_common.parse_driver_info(task.node) + + def get_supported_boot_devices(self): + """Get a list of the supported boot devices. + + :returns: A list with the supported boot devices defined + in :mod:`ironic.common.boot_devices`. + + """ + return list(BOOT_DEVICE_MAPPING_TO_ILO.keys()) + + def get_boot_device(self, task): + """Get the current boot device for a node. + + Returns the current boot device of the node. + + :param task: a task from TaskManager. + :raises: MissingParameterValue if a required iLO parameter is missing. + :raises: IloOperationError on an error from IloClient library. + :returns: a dictionary containing: + + :boot_device: + the boot device, one of the supported devices listed in + :mod:`ironic.common.boot_devices` or None if it is unknown. + :persistent: + Whether the boot device will persist to all future boots or + not, None if it is unknown. + + """ + ilo_object = ilo_common.get_ilo_object(task.node) + persistent = False + + try: + # Return one time boot device if set, else return + # the persistent boot device + next_boot = ilo_object.get_one_time_boot() + if next_boot == 'Normal': + # One time boot is not set. Check for persistent boot. + persistent = True + next_boot = ilo_object.get_persistent_boot_device() + + except ilo_client.IloError as ilo_exception: + operation = _("Get boot device") + raise exception.IloOperationError(operation=operation, + error=ilo_exception) + + boot_device = BOOT_DEVICE_ILO_TO_GENERIC.get(next_boot, None) + + if boot_device is None: + persistent = None + + return {'boot_device': boot_device, 'persistent': persistent} + + @task_manager.require_exclusive_lock + def set_boot_device(self, task, device, persistent=False): + """Set the boot device for a node. + + Set the boot device to use on next reboot of the node. + + :param task: a task from TaskManager. + :param device: the boot device, one of the supported devices + listed in :mod:`ironic.common.boot_devices`. + :param persistent: Boolean value. True if the boot device will + persist to all future boots, False if not. + Default: False. + :raises: InvalidParameterValue if an invalid boot device is + specified. + :raises: MissingParameterValue if a required parameter is missing. + :raises: IloOperationError on an error from IloClient library. + """ + + try: + boot_device = BOOT_DEVICE_MAPPING_TO_ILO[device] + except KeyError: + raise exception.InvalidParameterValue(_( + "Invalid boot device %s specified.") % device) + try: + ilo_object = ilo_common.get_ilo_object(task.node) + + if not persistent: + ilo_object.set_one_time_boot(boot_device) + else: + ilo_object.update_persistent_boot([boot_device]) + + except ilo_client.IloError as ilo_exception: + operation = _("Setting %s as boot device") % device + raise exception.IloOperationError(operation=operation, + error=ilo_exception) + + LOG.debug("Node %(uuid)s set to boot from %(device)s.", + {'uuid': task.node.uuid, 'device': device}) + + def get_sensors_data(self, task): + """Get sensors data. + + :param task: a TaskManager instance. + :raises: FailedToGetSensorData when getting the sensor data fails. + :raises: FailedToParseSensorData when parsing sensor data fails. + :raises: InvalidParameterValue if required ipmi parameters + are missing. + :raises: MissingParameterValue if a required parameter is missing. + :returns: returns a dict of sensor data group by sensor type. + + """ + ilo_common.update_ipmi_properties(task) + ipmi_management = ipmitool.IPMIManagement() + return ipmi_management.get_sensors_data(task) diff --git a/ironic/drivers/modules/ilo/power.py b/ironic/drivers/modules/ilo/power.py index b799fb69ab..dd67ce8f6a 100644 --- a/ironic/drivers/modules/ilo/power.py +++ b/ironic/drivers/modules/ilo/power.py @@ -19,11 +19,13 @@ iLO Power Driver from oslo.config import cfg from oslo.utils import importutils +from ironic.common import boot_devices from ironic.common import exception from ironic.common.i18n import _ from ironic.common.i18n import _LE from ironic.common import states from ironic.conductor import task_manager +from ironic.conductor import utils as manager_utils from ironic.drivers import base from ironic.drivers.modules.ilo import common as ilo_common from ironic.openstack.common import log as logging @@ -61,7 +63,7 @@ def _attach_boot_iso(task): if 'ilo_boot_iso' in i_info: ilo_common.setup_vmedia_for_boot(task, i_info['ilo_boot_iso']) - ilo_common.set_boot_device(task.node, 'CDROM') + manager_utils.node_set_boot_device(task, boot_devices.CDROM) def _get_power_state(node): diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py index 130df0d9a2..c843877528 100644 --- a/ironic/drivers/pxe.py +++ b/ironic/drivers/pxe.py @@ -24,6 +24,7 @@ from ironic.common.i18n import _ from ironic.drivers import base from ironic.drivers.modules import iboot from ironic.drivers.modules.ilo import deploy as ilo_deploy +from ironic.drivers.modules.ilo import management as ilo_management from ironic.drivers.modules.ilo import power as ilo_power from ironic.drivers.modules import ipminative from ironic.drivers.modules import ipmitool @@ -147,8 +148,7 @@ class PXEAndIloDriver(base.BaseDriver): This driver implements the `core` functionality using :class:`ironic.drivers.modules.ilo.power.IloPower` for power management - :class:`ironic.drivers.modules.ilo.deploy.IloPXEDeploy` - :class:`ironic.drivers.modules.ilo.deploy.IloManagement` for image + :class:`ironic.drivers.modules.ilo.deploy.IloPXEDeploy` for image deployment. """ @@ -162,7 +162,7 @@ class PXEAndIloDriver(base.BaseDriver): self.deploy = ilo_deploy.IloPXEDeploy() self.vendor = ilo_deploy.IloPXEVendorPassthru() self.console = ilo_deploy.IloConsoleInterface() - self.management = ilo_deploy.IloManagement() + self.management = ilo_management.IloManagement() class PXEAndSNMPDriver(base.BaseDriver): diff --git a/ironic/tests/drivers/ilo/test_common.py b/ironic/tests/drivers/ilo/test_common.py index 67c806b2ef..7575b71988 100644 --- a/ironic/tests/drivers/ilo/test_common.py +++ b/ironic/tests/drivers/ilo/test_common.py @@ -155,6 +155,23 @@ class IloCommonMethodsTestCase(db_base.DbTestCase): ilo_common.get_ilo_license, self.node) + def test_update_ipmi_properties(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + ipmi_info = { + "ipmi_address": "1.2.3.4", + "ipmi_username": "admin", + "ipmi_password": "fake", + "ipmi_terminal_port": 60 + } + ilo_info = INFO_DICT + ilo_info['console_port'] = 60 + task.node.driver_info = ilo_info + ilo_common.update_ipmi_properties(task) + actual_info = task.node.driver_info + expected_info = dict(INFO_DICT, **ipmi_info) + self.assertEqual(expected_info, actual_info) + def test__get_floppy_image_name(self): image_name_expected = 'image-' + self.node.uuid image_name_actual = ilo_common._get_floppy_image_name(self.node) @@ -248,20 +265,6 @@ class IloCommonMethodsTestCase(db_base.DbTestCase): self.assertRaises(exception.IloOperationError, ilo_common.attach_vmedia, self.node, 'FLOPPY', 'url') - @mock.patch.object(ilo_common, 'get_ilo_object') - def test_set_boot_device(self, get_ilo_object_mock): - ilo_object_mock = get_ilo_object_mock.return_value - ilo_common.set_boot_device(self.node, 'CDROM') - get_ilo_object_mock.assert_called_once_with(self.node) - ilo_object_mock.set_one_time_boot.assert_called_once_with('CDROM') - - @mock.patch.object(ilo_common, 'get_ilo_object') - def test_set_boot_device_persistent_true(self, get_ilo_object_mock): - ilo_mock = get_ilo_object_mock.return_value - ilo_common.set_boot_device(self.node, 'NETWORK', True) - get_ilo_object_mock.assert_called_once_with(self.node) - ilo_mock.update_persistent_boot.assert_called_once_with(['NETWORK']) - @mock.patch.object(ilo_common, 'get_ilo_object') def test_set_boot_mode(self, get_ilo_object_mock): ilo_object_mock = get_ilo_object_mock.return_value diff --git a/ironic/tests/drivers/ilo/test_deploy.py b/ironic/tests/drivers/ilo/test_deploy.py index 54d25211ac..d024d9fbf7 100644 --- a/ironic/tests/drivers/ilo/test_deploy.py +++ b/ironic/tests/drivers/ilo/test_deploy.py @@ -20,7 +20,7 @@ import tempfile import mock from oslo.config import cfg -from ironic.common import exception +from ironic.common import boot_devices from ironic.common import images from ironic.common import states from ironic.common import swift @@ -180,7 +180,7 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): self.assertEqual(expected_info, actual_info) @mock.patch.object(manager_utils, 'node_power_action') - @mock.patch.object(ilo_common, 'set_boot_device') + @mock.patch.object(manager_utils, 'node_set_boot_device') @mock.patch.object(ilo_common, 'setup_vmedia_for_boot') def test__reboot_into(self, setup_vmedia_mock, set_boot_device_mock, node_power_action_mock): @@ -189,7 +189,8 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): opts = {'a': 'b'} ilo_deploy._reboot_into(task, 'iso', opts) setup_vmedia_mock.assert_called_once_with(task, 'iso', opts) - set_boot_device_mock.assert_called_once_with(task.node, 'CDROM') + set_boot_device_mock.assert_called_once_with(task, + boot_devices.CDROM) node_power_action_mock.assert_called_once_with(task, states.REBOOT) @@ -222,12 +223,11 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @mock.patch.object(ilo_deploy, '_get_single_nic_with_vif_port_id') @mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options') @mock.patch.object(manager_utils, 'node_power_action') - @mock.patch.object(ilo_common, 'set_boot_device') @mock.patch.object(iscsi_deploy, 'check_image_size') @mock.patch.object(iscsi_deploy, 'cache_instance_image') def test_deploy(self, cache_instance_image_mock, check_image_size_mock, - set_boot_device_mock, node_power_action_mock, - build_opts_mock, get_nic_mock, reboot_into_mock): + node_power_action_mock, build_opts_mock, get_nic_mock, + reboot_into_mock): deploy_opts = {'a': 'b'} build_opts_mock.return_value = deploy_opts get_nic_mock.return_value = '12:34:56:78:90:ab' @@ -328,7 +328,7 @@ class VendorPassthruTestCase(db_base.DbTestCase): driver='iscsi_ilo', driver_info=INFO_DICT) @mock.patch.object(deploy_utils, 'notify_deploy_complete') - @mock.patch.object(ilo_common, 'set_boot_device') + @mock.patch.object(manager_utils, 'node_set_boot_device') @mock.patch.object(ilo_common, 'setup_vmedia_for_boot') @mock.patch.object(ilo_deploy, '_get_boot_iso') @mock.patch.object(iscsi_deploy, 'continue_deploy') @@ -351,7 +351,8 @@ class VendorPassthruTestCase(db_base.DbTestCase): continue_deploy_mock.assert_called_once_with(task, **kwargs) get_boot_iso_mock.assert_called_once_with(task, 'root-uuid') setup_vmedia_mock.assert_called_once_with(task, 'boot-iso') - set_boot_device_mock.assert_called_once_with(task.node, 'CDROM') + set_boot_device_mock.assert_called_once_with(task, + boot_devices.CDROM) self.assertEqual('boot-iso', task.node.instance_info['ilo_boot_iso']) notify_deploy_complete_mock.assert_called_once_with('123456') @@ -430,41 +431,16 @@ class IloPXEDeployTestCase(db_base.DbTestCase): pxe_prepare_mock.assert_called_once_with(task) @mock.patch.object(pxe.PXEDeploy, 'deploy') - @mock.patch.object(ilo_common, 'set_boot_device') + @mock.patch.object(manager_utils, 'node_set_boot_device') def test_deploy_boot_mode_exists(self, set_persistent_mock, pxe_deploy_mock): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: task.driver.deploy.deploy(task) - set_persistent_mock.assert_called_with(task.node, 'NETWORK', False) + set_persistent_mock.assert_called_with(task, boot_devices.PXE) pxe_deploy_mock.assert_called_once_with(task) -class IloManagementTestCase(db_base.DbTestCase): - - def setUp(self): - super(IloManagementTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver="pxe_ilo") - self.node = obj_utils.create_test_node(self.context, - driver='pxe_ilo', driver_info=INFO_DICT) - - @mock.patch.object(ilo_common, 'set_boot_device') - def test_set_boot_device_ok(self, set_persistent_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.management.set_boot_device(task, 'pxe', True) - set_persistent_mock.assert_called_once_with(task.node, - 'NETWORK', True) - - @mock.patch.object(ilo_common, 'set_boot_device') - def test_set_boot_device_invalid_device(self, set_persistent_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.assertRaises(exception.InvalidParameterValue, - task.driver.management.set_boot_device, - task, 'fake-device') - - class IloPXEVendorPassthruTestCase(db_base.DbTestCase): def setUp(self): @@ -489,13 +465,14 @@ class IloPXEVendorPassthruTestCase(db_base.DbTestCase): self.assertEqual({}, driver_routes) @mock.patch.object(pxe.VendorPassthru, '_continue_deploy') - @mock.patch.object(ilo_common, 'set_boot_device') - def test_vendorpassthru(self, set_persistent_mock, + @mock.patch.object(manager_utils, 'node_set_boot_device') + def test_vendorpassthru(self, set_boot_device_mock, pxe_vendorpassthru_mock): kwargs = {'address': '123456'} with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: task.node.provision_state = states.DEPLOYWAIT task.driver.vendor._continue_deploy(task, **kwargs) - set_persistent_mock.assert_called_with(task.node, 'NETWORK', True) + set_boot_device_mock.assert_called_with(task, boot_devices.PXE, + True) pxe_vendorpassthru_mock.assert_called_once_with(task, **kwargs) diff --git a/ironic/tests/drivers/ilo/test_management.py b/ironic/tests/drivers/ilo/test_management.py new file mode 100644 index 0000000000..a01abd026e --- /dev/null +++ b/ironic/tests/drivers/ilo/test_management.py @@ -0,0 +1,195 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# 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. + + +"""Test class for Management Interface used by iLO modules.""" + +import mock +from oslo.config import cfg +from oslo.utils import importutils + +from ironic.common import boot_devices +from ironic.common import exception +from ironic.conductor import task_manager +from ironic.drivers.modules.ilo import common as ilo_common +from ironic.drivers.modules.ilo import management as ilo_management +from ironic.drivers.modules import ipmitool +from ironic.tests.conductor import utils as mgr_utils +from ironic.tests.db import base as db_base +from ironic.tests.db import utils as db_utils +from ironic.tests.objects import utils as obj_utils + +ilo_client = importutils.try_import('proliantutils.ilo.ribcl') + + +INFO_DICT = db_utils.get_test_ilo_info() +CONF = cfg.CONF + + +class IloManagementTestCase(db_base.DbTestCase): + + def setUp(self): + super(IloManagementTestCase, self).setUp() + mgr_utils.mock_the_extension_manager(driver="fake_ilo") + self.node = obj_utils.create_test_node(self.context, + driver='fake_ilo', driver_info=INFO_DICT) + + def test_get_properties(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + expected = ilo_common.REQUIRED_PROPERTIES + self.assertEqual(expected, task.driver.management. + get_properties()) + + @mock.patch.object(ilo_common, 'parse_driver_info') + def test_validate(self, driver_info_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.management.validate(task) + driver_info_mock.assert_called_once_with(task.node) + + def test_get_supported_boot_devices(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + expected = [boot_devices.PXE, boot_devices.DISK, + boot_devices.CDROM] + self.assertEqual(sorted(expected), sorted(task.driver.management. + get_supported_boot_devices())) + + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_get_boot_device_next_boot(self, get_ilo_object_mock): + ilo_object_mock = get_ilo_object_mock.return_value + ilo_object_mock.get_one_time_boot.return_value = 'CDROM' + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + expected_device = boot_devices.CDROM + expected_response = {'boot_device': expected_device, + 'persistent': False} + self.assertEqual(expected_response, + task.driver.management.get_boot_device(task)) + ilo_object_mock.get_one_time_boot.assert_called_once_with() + + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_get_boot_device_persistent(self, get_ilo_object_mock): + ilo_mock = get_ilo_object_mock.return_value + ilo_mock.get_one_time_boot.return_value = 'Normal' + ilo_mock.get_persistent_boot_device.return_value = 'NETWORK' + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + expected_device = boot_devices.PXE + expected_response = {'boot_device': expected_device, + 'persistent': True} + self.assertEqual(expected_response, + task.driver.management.get_boot_device(task)) + ilo_mock.get_one_time_boot.assert_called_once_with() + ilo_mock.get_persistent_boot_device.assert_called_once_with() + + @mock.patch.object(ilo_management, 'ilo_client') + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_get_boot_device_fail(self, get_ilo_object_mock, ilo_mgmt_mock): + ilo_mgmt_mock.IloError = Exception + ilo_mock_object = get_ilo_object_mock.return_value + ilo_mock_object.get_one_time_boot.side_effect = Exception() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.IloOperationError, + task.driver.management.get_boot_device, + task) + ilo_mock_object.get_one_time_boot.assert_called_once_with() + + @mock.patch.object(ilo_management, 'ilo_client') + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_get_boot_device_persistent_fail(self, get_ilo_object_mock, + ilo_mgmt_mock): + ilo_mgmt_mock.IloError = Exception + ilo_mock_object = get_ilo_object_mock.return_value + ilo_mock_object.get_one_time_boot.return_value = 'Normal' + ilo_mock_object.get_persistent_boot_device.side_effect = Exception() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.IloOperationError, + task.driver.management.get_boot_device, + task) + ilo_mock_object.get_one_time_boot.assert_called_once_with() + ilo_mock_object.get_persistent_boot_device.assert_called_once_with() + + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_set_boot_device_ok(self, get_ilo_object_mock): + ilo_object_mock = get_ilo_object_mock.return_value + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.management.set_boot_device(task, boot_devices.CDROM, + False) + get_ilo_object_mock.assert_called_once_with(task.node) + ilo_object_mock.set_one_time_boot.assert_called_once_with('CDROM') + + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_set_boot_device_persistent_true(self, get_ilo_object_mock): + ilo_mock = get_ilo_object_mock.return_value + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.management.set_boot_device(task, boot_devices.PXE, + True) + get_ilo_object_mock.assert_called_once_with(task.node) + ilo_mock.update_persistent_boot.assert_called_once_with( + ['NETWORK']) + + @mock.patch.object(ilo_management, 'ilo_client') + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_set_boot_device_fail(self, get_ilo_object_mock, ilo_mgmt_mock): + ilo_mgmt_mock.IloError = Exception + ilo_mock_object = get_ilo_object_mock.return_value + ilo_mock_object.set_one_time_boot.side_effect = Exception() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.IloOperationError, + task.driver.management.set_boot_device, + task, boot_devices.PXE) + ilo_mock_object.set_one_time_boot.assert_called_once_with('NETWORK') + + @mock.patch.object(ilo_management, 'ilo_client') + @mock.patch.object(ilo_common, 'get_ilo_object') + def test_set_boot_device_persistent_fail(self, get_ilo_object_mock, + ilo_mgmt_mock): + ilo_mgmt_mock.IloError = Exception + ilo_mock_object = get_ilo_object_mock.return_value + ilo_mock_object.update_persistent_boot.side_effect = Exception() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.IloOperationError, + task.driver.management.set_boot_device, + task, boot_devices.PXE, True) + ilo_mock_object.update_persistent_boot.assert_called_once_with( + ['NETWORK']) + + def test_set_boot_device_invalid_device(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.InvalidParameterValue, + task.driver.management.set_boot_device, + task, 'fake-device') + + @mock.patch.object(ilo_common, 'update_ipmi_properties') + @mock.patch.object(ipmitool.IPMIManagement, 'get_sensors_data') + def test_get_sensor_data(self, get_sensors_data_mock, update_ipmi_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.management.get_sensors_data(task) + update_ipmi_mock.assert_called_once_with(task) + get_sensors_data_mock.assert_called_once_with(task) diff --git a/ironic/tests/drivers/ilo/test_power.py b/ironic/tests/drivers/ilo/test_power.py index e1a8357dcc..a80ff7e7e6 100644 --- a/ironic/tests/drivers/ilo/test_power.py +++ b/ironic/tests/drivers/ilo/test_power.py @@ -19,9 +19,11 @@ import mock from oslo.config import cfg from oslo.utils import importutils +from ironic.common import boot_devices from ironic.common import exception from ironic.common import states from ironic.conductor import task_manager +from ironic.conductor import utils as manager_utils from ironic.drivers.modules.ilo import common as ilo_common from ironic.drivers.modules.ilo import deploy as ilo_deploy from ironic.drivers.modules.ilo import power as ilo_power @@ -142,7 +144,7 @@ class IloPowerInternalMethodsTestCase(db_base.DbTestCase): ilo_mock_object.get_host_power_status.assert_called_with() ilo_mock_object.set_host_power.assert_called_once_with('ON') - @mock.patch.object(ilo_common, 'set_boot_device') + @mock.patch.object(manager_utils, 'node_set_boot_device') @mock.patch.object(ilo_common, 'setup_vmedia_for_boot') def test__attach_boot_iso(self, setup_vmedia_mock, set_boot_device_mock, power_ilo_client_mock, common_ilo_client_mock): @@ -151,7 +153,8 @@ class IloPowerInternalMethodsTestCase(db_base.DbTestCase): task.node.instance_info['ilo_boot_iso'] = 'boot-iso' ilo_power._attach_boot_iso(task) setup_vmedia_mock.assert_called_once_with(task, 'boot-iso') - set_boot_device_mock.assert_called_once_with(task.node, 'CDROM') + set_boot_device_mock.assert_called_once_with(task, + boot_devices.CDROM) class IloPowerTestCase(db_base.DbTestCase):