Merge "iLO implementation for hardware inspection"

This commit is contained in:
Jenkins 2015-03-18 18:49:21 +00:00 committed by Gerrit Code Review
commit 930f479dc1
8 changed files with 728 additions and 6 deletions

View File

@ -30,6 +30,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 inspect as ilo_inspect
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
@ -152,6 +153,7 @@ class FakeIloDriver(base.BaseDriver):
self.power = ilo_power.IloPower()
self.deploy = fake.FakeDeploy()
self.management = ilo_management.IloManagement()
self.inspect = ilo_inspect.IloInspect()
class FakeDracDriver(base.BaseDriver):

View File

@ -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 inspect
from ironic.drivers.modules.ilo import management
from ironic.drivers.modules.ilo import power
@ -47,6 +48,7 @@ class IloVirtualMediaIscsiDriver(base.BaseDriver):
self.console = deploy.IloConsoleInterface()
self.management = management.IloManagement()
self.vendor = deploy.VendorPassthru()
self.inspect = inspect.IloInspect()
class IloVirtualMediaAgentDriver(base.BaseDriver):
@ -70,3 +72,4 @@ class IloVirtualMediaAgentDriver(base.BaseDriver):
self.console = deploy.IloConsoleInterface()
self.management = management.IloManagement()
self.vendor = agent.AgentVendorInterface()
self.inspect = inspect.IloInspect()

View File

@ -75,6 +75,15 @@ CONSOLE_PROPERTIES = {
'console_port': _("node's UDP port to connect to. Only required for "
"console access.")
}
INSPECT_PROPERTIES = {
'inspect_ports': _("Comma-separated values of ethernet ports "
"to be identified for creating node "
"ports. Valid values may be "
"inspect_ports = '1,2,...n' or "
"inspect_ports = 'all' or "
"inspect_ports = 'none'. "
"Required only for inspection.")
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
@ -128,6 +137,11 @@ def parse_driver_info(node):
except ValueError:
not_integers.append(param)
for param in INSPECT_PROPERTIES:
value = info.get(param)
if value:
d_info[param] = value
if not_integers:
raise exception.InvalidParameterValue(_(
"The following iLO parameters from the node's driver_info "

View File

@ -0,0 +1,306 @@
# 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 Inspect Interface
"""
from oslo_utils import importutils
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common.i18n import _LI
from ironic.common.i18n import _LW
from ironic.common import states
from ironic.conductor import utils as conductor_utils
from ironic.db import api as dbapi
from ironic.drivers import base
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.openstack.common import log as logging
ilo_error = importutils.try_import('proliantutils.exception')
LOG = logging.getLogger(__name__)
ESSENTIAL_PROPERTIES_KEYS = {'memory_mb', 'local_gb', 'cpus', 'cpu_arch'}
def _create_ports_if_not_exist(node, macs):
"""Create ironic ports for the mac addresses.
Creates ironic ports for the mac addresses returned with inspection
or as requested by operator.
:param node: node object.
:param macs: A dictionary of port numbers to mac addresses
returned by node inspection.
"""
node_id = node.id
sql_dbapi = dbapi.get_instance()
for mac in macs.values():
port_dict = {'address': mac, 'node_id': node_id}
try:
sql_dbapi.create_port(port_dict)
LOG.info(_LI("Port created for MAC address %(address)s for node "
"%(node)s"), {'address': mac, 'node': node.uuid})
except exception.MACAlreadyExists:
LOG.warn(_LW("Port already exists for MAC address %(address)s "
"for node %(node)s"), {'address': mac,
'node': node.uuid})
def _get_essential_properties(node, ilo_object):
"""Inspects the node and get essential scheduling properties
:param node: node object.
:param ilo_object: an instance of proliantutils.ilo.IloClient
:raises: HardwareInspectionFailure if any of the properties values
are missing.
:returns: The dictionary containing properties and MAC data.
The dictionary possible keys are 'properties' and 'macs'.
The 'properties' should contain keys as in
ESSENTIAL_PROPERTIES_KEYS. The 'macs' is a dictionary
containing key:value pairs of <port_numbers:mac_addresses>
"""
try:
# Retrieve the mandatory properties from hardware
result = ilo_object.get_essential_properties()
except ilo_error.IloError as e:
raise exception.HardwareInspectionFailure(error=e)
_validate(node, result)
return result
def _validate(node, data):
"""Validate the received value against the supported keys in ironic.
:param node: node object.
:param data: the dictionary received by querying server.
:raises: HardwareInspectionFailure
"""
if data.get('properties'):
if isinstance(data['properties'], dict):
valid_keys = ESSENTIAL_PROPERTIES_KEYS
missing_keys = valid_keys - set(data['properties'])
if missing_keys:
error = (_(
"Server didn't return the key(s): %(key)s") %
{'key': ', '.join(missing_keys)})
raise exception.HardwareInspectionFailure(error=error)
else:
error = (_("Essential properties are expected to be in dictionary "
"format, received %(properties)s from node "
"%(node)s.") % {"properties": data['properties'],
'node': node.uuid})
raise exception.HardwareInspectionFailure(error=error)
else:
error = (_("The node %s didn't return 'properties' as the key with "
"inspection.") % node.uuid)
raise exception.HardwareInspectionFailure(error=error)
if data.get('macs'):
if not isinstance(data['macs'], dict):
error = (_("Node %(node)s didn't return MACs %(macs)s "
"in dictionary format.")
% {"macs": data['macs'], 'node': node.uuid})
raise exception.HardwareInspectionFailure(error=error)
else:
error = (_("The node %s didn't return 'macs' as the key with "
"inspection.") % node.uuid)
raise exception.HardwareInspectionFailure(error=error)
def _get_macs_for_desired_ports(node, macs):
"""Get the dict of MACs which are desired by the operator.
Get the MACs for desired ports.
Returns a dictionary of MACs associated with the ports specified
in the node's driver_info/inspect_ports.
The driver_info field is expected to be populated with
comma-separated port numbers like driver_info/inspect_ports='1,2'.
In this case the inspection is expected to create ironic ports
only for these two ports.
The proliantutils is expected to return key value pair for each
MAC address like:
{'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
Possible scenarios:
'inspect_ports' == 'all' : creates ports for all inspected MACs
'inspect_ports' == <valid_port_numbers>: creates ports for
requested port numbers.
'inspect_ports' == <mix_of_valid_invalid> : raise error for
invalid inputs.
'inspect_ports' == 'none' : doesnt do any action with the
inspected mac addresses.
This method is not called if 'inspect_ports' == 'none', hence the
scenario is not covered under this method.
:param node: a node object.
:param macs: a dictionary of MAC addresses returned by the hardware
with inspection.
:returns: a dictionary of port numbers and MAC addresses with only
the MACs requested by operator in
node.driver_info['inspect_ports']
:raises: HardwareInspectionFailure for the non-existing ports
requested in node.driver_info['inspect_ports']
"""
driver_info = node.driver_info
desired_macs = str(driver_info.get('inspect_ports'))
# If the operator has given 'all' just return all the macs
# returned by inspection.
if desired_macs.lower() == 'all':
to_be_created_macs = macs
else:
to_be_created_macs = {}
# desired_macs_list = desired_macs.split(',')
# The list should look like ['Port 1', 'Port 2'] as
# iLO returns port numbers like this.
desired_macs_list = [
'Port %s' % port_number
for port_number in (desired_macs.split(','))]
# Check if the given input is valid or not. Return all the
# requested macs.
non_existing_ports = []
for port_number in desired_macs_list:
mac_address = macs.get(port_number)
if mac_address:
to_be_created_macs[port_number] = mac_address
else:
non_existing_ports.append(port_number)
# It is possible that operator has given a wrong input by mistake.
if non_existing_ports:
error = (_("Could not find requested ports %(ports)s on the "
"node %(node)s")
% {'ports': non_existing_ports, 'node': node.uuid})
raise exception.HardwareInspectionFailure(error=error)
return to_be_created_macs
class IloInspect(base.InspectInterface):
def get_properties(self):
d = ilo_common.REQUIRED_PROPERTIES.copy()
d.update(ilo_common.INSPECT_PROPERTIES)
return d
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.
:raises: InvalidParameterValue if invalid input provided.
"""
node = task.node
driver_info = ilo_common.parse_driver_info(node)
if 'inspect_ports' not in driver_info:
raise exception.MissingParameterValue(_(
"Missing 'inspect_ports' parameter in node's driver_info."))
value = driver_info['inspect_ports']
if (value.lower() != 'all' and value.lower() != 'none'
and not all(s.isdigit() for s in value.split(','))):
raise exception.InvalidParameterValue(_(
"inspect_ports can accept either comma separated "
"port numbers, or a single port number, or 'all' "
"or 'none'. %(value)s given for node %(node)s "
"driver_info['inspect_ports']")
% {'value': value, 'node': node})
def inspect_hardware(self, task):
"""Inspect hardware to get the hardware properties.
Inspects hardware to get the essential and additional hardware
properties. It fails if any of the essential properties
are not received from the node or if 'inspect_ports' is
not provided in driver_info.
It doesnt fail if node fails to return any capabilities as
the capabilities differ from hardware to hardware mostly.
:param task: a TaskManager instance.
:raises: HardwareInspectionFailure if essential properties
could not be retrieved successfully.
:raises: IloOperationError if system fails to get power state.
:returns: The resulting state of inspection.
"""
power_turned_on = False
ilo_object = ilo_common.get_ilo_object(task.node)
try:
state = task.driver.power.get_power_state(task)
except exception.IloOperationError as ilo_exception:
operation = (_("Inspecting hardware (get_power_state) on %s")
% task.node.uuid)
raise exception.IloOperationError(operation=operation,
error=ilo_exception)
if state != states.POWER_ON:
LOG.info(_LI("The node %s is not powered on. Powering on the "
"node for inspection."), task.node.uuid)
conductor_utils.node_power_action(task, states.POWER_ON)
power_turned_on = True
# get the essential properties and update the node properties
# with it.
inspected_properties = {}
result = _get_essential_properties(task.node, ilo_object)
properties = result['properties']
for known_property in ESSENTIAL_PROPERTIES_KEYS:
inspected_properties[known_property] = properties[known_property]
node_properties = task.node.properties
node_properties.update(inspected_properties)
task.node.properties = node_properties
task.node.save()
# Get the desired node inputs from the driver_info and create ports
# as requested. It doesnt delete the ports because there is
# no way for the operator to know which all MACs are associated
# with the node and which are not. The proliantutils can
# return only embedded NICs mac addresses and not the STANDUP NIC
# cards. The port creation code is not excercised if
# 'inspect_ports' == 'none'.
driver_info = task.node.driver_info
if (driver_info['inspect_ports']).lower() != 'none':
macs_input_given = (
_get_macs_for_desired_ports(task.node, result['macs']))
if macs_input_given:
# Create ports only for the requested ports.
_create_ports_if_not_exist(task.node, macs_input_given)
LOG.debug(("Node properties for %(node)s are updated as "
"%(properties)s"),
{'properties': inspected_properties,
'node': task.node.uuid})
LOG.info(_LI("Node %s inspected."), task.node.uuid)
if power_turned_on:
conductor_utils.node_power_action(task, states.POWER_OFF)
LOG.info(_LI("The node %s was powered on for inspection. "
"Powered off the node as inspection completed."),
task.node.uuid)
return states.MANAGEABLE

View File

@ -28,6 +28,7 @@ from ironic.drivers.modules.amt import vendor as amt_vendor
from ironic.drivers.modules import discoverd
from ironic.drivers.modules import iboot
from ironic.drivers.modules.ilo import deploy as ilo_deploy
from ironic.drivers.modules.ilo import inspect as ilo_inspect
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
@ -176,6 +177,7 @@ class PXEAndIloDriver(base.BaseDriver):
self.vendor = ilo_deploy.IloPXEVendorPassthru()
self.console = ilo_deploy.IloConsoleInterface()
self.management = ilo_management.IloManagement()
self.inspect = ilo_inspect.IloInspect()
class PXEAndSNMPDriver(base.BaseDriver):

View File

@ -3339,19 +3339,19 @@ class ManagerTestProperties(tests_db_base.DbTestCase):
def test_driver_properties_fake_ilo(self):
expected = ['ilo_address', 'ilo_username', 'ilo_password',
'client_port', 'client_timeout']
'client_port', 'client_timeout', 'inspect_ports']
self._check_driver_properties("fake_ilo", expected)
def test_driver_properties_ilo_iscsi(self):
expected = ['ilo_address', 'ilo_username', 'ilo_password',
'client_port', 'client_timeout', 'ilo_deploy_iso',
'console_port']
'console_port', 'inspect_ports']
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',
'console_port']
'console_port', 'inspect_ports']
self._check_driver_properties("agent_ilo", expected)
def test_driver_properties_fail(self):

View File

@ -0,0 +1,396 @@
# 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 ironic.common import exception
from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as conductor_utils
from ironic.db import api as dbapi
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules.ilo import inspect as ilo_inspect
from ironic.drivers.modules.ilo import power as ilo_power
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
INFO_DICT = db_utils.get_test_ilo_info()
CONF = cfg.CONF
class IloInspectTestCase(db_base.DbTestCase):
def setUp(self):
super(IloInspectTestCase, 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:
properties = ilo_common.REQUIRED_PROPERTIES.copy()
properties.update(ilo_common.INSPECT_PROPERTIES)
self.assertEqual(properties,
task.driver.inspect.get_properties())
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate_inspect_ports_valid_with_comma(self, driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
driver_info_mock.return_value = {'inspect_ports': '1,2'}
task.driver.inspect.validate(task)
driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate_inspect_ports_valid_None(self, driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
driver_info_mock.return_value = {'inspect_ports': 'None'}
task.driver.inspect.validate(task)
driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate_inspect_ports_valid_all(self, driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
driver_info_mock.return_value = {'inspect_ports': 'all'}
task.driver.inspect.validate(task)
driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate_inspect_ports_valid_single(self, driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
driver_info_mock.return_value = {'inspect_ports': '1'}
task.driver.inspect.validate(task)
driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate_inspect_ports_invalid(self, driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
driver_info_mock.return_value = {'inspect_ports': 'abc'}
self.assertRaises(exception.InvalidParameterValue,
task.driver.inspect.validate, task)
driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate_inspect_ports_missing(self, driver_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
driver_info_mock.return_value = {'xyz': 'abc'}
self.assertRaises(exception.MissingParameterValue,
task.driver.inspect.validate, task)
driver_info_mock.assert_called_once_with(task.node)
@mock.patch.object(ilo_inspect, '_create_ports_if_not_exist')
@mock.patch.object(ilo_inspect, '_get_macs_for_desired_ports')
@mock.patch.object(ilo_inspect, '_get_essential_properties')
@mock.patch.object(ilo_power.IloPower, 'get_power_state')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_inspect_essential_ok(self, get_ilo_object_mock,
power_mock,
get_essential_mock,
desired_macs_mock,
create_port_mock):
ilo_object_mock = get_ilo_object_mock.return_value
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
desired_macs_mock.return_value = {'Port 1': 'aa:aa:aa:aa:aa:aa',
'Port 2': 'bb:bb:bb:bb:bb:bb'}
result = {'properties': properties, 'macs': macs}
get_essential_mock.return_value = result
power_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info = {'inspect_ports': 'all'}
task.driver.inspect.inspect_hardware(task)
self.assertEqual(properties, task.node.properties)
power_mock.assert_called_once_with(task)
get_essential_mock.assert_called_once_with(task.node,
ilo_object_mock)
create_port_mock.assert_called_once_with(task.node, macs)
@mock.patch.object(ilo_inspect, '_create_ports_if_not_exist')
@mock.patch.object(ilo_inspect, '_get_macs_for_desired_ports')
@mock.patch.object(ilo_inspect, '_get_essential_properties')
@mock.patch.object(conductor_utils, 'node_power_action')
@mock.patch.object(ilo_power.IloPower, 'get_power_state')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_inspect_essential_ok_power_off(self, get_ilo_object_mock,
power_mock,
set_power_mock,
get_essential_mock,
desired_macs_mock,
create_port_mock):
ilo_object_mock = get_ilo_object_mock.return_value
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
desired_macs_mock.return_value = {'Port 1': 'aa:aa:aa:aa:aa:aa',
'Port 2': 'bb:bb:bb:bb:bb:bb'}
result = {'properties': properties, 'macs': macs}
get_essential_mock.return_value = result
power_mock.return_value = states.POWER_OFF
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info = {'inspect_ports': 'all'}
task.driver.inspect.inspect_hardware(task)
self.assertEqual(properties, task.node.properties)
power_mock.assert_called_once_with(task)
set_power_mock.assert_any_call(task, states.POWER_ON)
get_essential_mock.assert_called_once_with(task.node,
ilo_object_mock)
create_port_mock.assert_called_once_with(task.node, macs)
@mock.patch.object(ilo_inspect, '_create_ports_if_not_exist')
@mock.patch.object(ilo_inspect, '_get_macs_for_desired_ports')
@mock.patch.object(ilo_inspect, '_get_essential_properties')
@mock.patch.object(ilo_power.IloPower, 'get_power_state')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_inspect_hardware_port_desired(self, get_ilo_object_mock,
power_mock,
get_essential_mock,
desired_macs_mock,
create_port_mock):
ilo_object_mock = get_ilo_object_mock.return_value
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
result = {'properties': properties, 'macs': macs}
macs_input_given = {'Port 1': 'aa:aa:aa:aa:aa:aa'}
desired_macs_mock.return_value = macs_input_given
get_essential_mock.return_value = result
power_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info = {'inspect_ports': '1'}
task.driver.inspect.inspect_hardware(task)
power_mock.assert_called_once_with(task)
get_essential_mock.assert_called_once_with(task.node,
ilo_object_mock)
self.assertEqual(task.node.properties, result['properties'])
create_port_mock.assert_called_once_with(task.node,
macs_input_given)
@mock.patch.object(ilo_inspect, '_create_ports_if_not_exist')
@mock.patch.object(ilo_inspect, '_get_macs_for_desired_ports')
@mock.patch.object(ilo_inspect, '_get_essential_properties')
@mock.patch.object(ilo_power.IloPower, 'get_power_state')
@mock.patch.object(ilo_common, 'get_ilo_object')
def test_inspect_hardware_port_desired_none(self, get_ilo_object_mock,
power_mock,
get_essential_mock,
desired_macs_mock,
create_port_mock):
ilo_object_mock = get_ilo_object_mock.return_value
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
result = {'properties': properties, 'macs': macs}
macs_input_given = {'Port 1': 'aa:aa:aa:aa:aa:aa'}
desired_macs_mock.return_value = macs_input_given
get_essential_mock.return_value = result
power_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info = {'inspect_ports': 'none'}
task.driver.inspect.inspect_hardware(task)
power_mock.assert_called_once_with(task)
get_essential_mock.assert_called_once_with(task.node,
ilo_object_mock)
self.assertEqual(task.node.properties, result['properties'])
create_port_mock.assert_not_called()
class TestInspectPrivateMethods(db_base.DbTestCase):
def setUp(self):
super(TestInspectPrivateMethods, 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)
@mock.patch.object(ilo_inspect.LOG, 'info')
@mock.patch.object(dbapi, 'get_instance')
def test__create_ports_if_not_exist(self, instance_mock, log_mock):
db_obj = instance_mock.return_value
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
node_id = self.node.id
port_dict1 = {'address': 'aa:aa:aa:aa:aa:aa', 'node_id': node_id}
port_dict2 = {'address': 'bb:bb:bb:bb:bb:bb', 'node_id': node_id}
ilo_inspect._create_ports_if_not_exist(self.node, macs)
instance_mock.assert_called_once()
self.assertTrue(log_mock.called)
db_obj.create_port.assert_any_call(port_dict1)
db_obj.create_port.assert_any_call(port_dict2)
@mock.patch.object(ilo_inspect.LOG, 'warn')
@mock.patch.object(dbapi, 'get_instance')
def test__create_ports_if_not_exist_mac_exception(self,
instance_mock,
log_mock):
dbapi_mock = instance_mock.return_value
dbapi_mock.create_port.side_effect = exception.MACAlreadyExists('f')
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
ilo_inspect._create_ports_if_not_exist(self.node, macs)
instance_mock.assert_called_once()
self.assertTrue(log_mock.called)
def test__get_essential_properties_ok(self):
ilo_mock = mock.MagicMock()
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
result = {'properties': properties, 'macs': macs}
ilo_mock.get_essential_properties.return_value = result
actual_result = ilo_inspect._get_essential_properties(self.node,
ilo_mock)
self.assertEqual(result, actual_result)
def test__get_essential_properties_fail(self):
ilo_mock = mock.MagicMock()
# Missing key: cpu_arch
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
result = {'properties': properties, 'macs': macs}
ilo_mock.get_essential_properties.return_value = result
result = self.assertRaises(exception.HardwareInspectionFailure,
ilo_inspect._get_essential_properties,
self.node,
ilo_mock)
self.assertEqual(
result.format_message(),
("Failed to inspect hardware. Reason: Server didn't return the "
"key(s): cpu_arch"))
def test__get_essential_properties_fail_invalid_format(self):
ilo_mock = mock.MagicMock()
# Not a dict
properties = ['memory_mb', '512', 'local_gb', '10',
'cpus', '1']
macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
capabilities = ''
result = {'properties': properties, 'macs': macs}
ilo_mock.get_essential_properties.return_value = result
ilo_mock.get_additional_capabilities.return_value = capabilities
self.assertRaises(exception.HardwareInspectionFailure,
ilo_inspect._get_essential_properties,
self.node, ilo_mock)
def test__get_essential_properties_fail_mac_invalid_format(self):
ilo_mock = mock.MagicMock()
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
# Not a dict
macs = 'aa:aa:aa:aa:aa:aa'
result = {'properties': properties, 'macs': macs}
ilo_mock.get_essential_properties.return_value = result
self.assertRaises(exception.HardwareInspectionFailure,
ilo_inspect._get_essential_properties,
self.node, ilo_mock)
def test__get_essential_properties_hardware_port_empty(self):
ilo_mock = mock.MagicMock()
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
# Not a dictionary
macs = None
result = {'properties': properties, 'macs': macs}
capabilities = ''
ilo_mock.get_essential_properties.return_value = result
ilo_mock.get_additional_capabilities.return_value = capabilities
self.assertRaises(exception.HardwareInspectionFailure,
ilo_inspect._get_essential_properties,
self.node, ilo_mock)
def test__get_essential_properties_hardware_port_not_dict(self):
ilo_mock = mock.MagicMock()
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1', 'cpu_arch': 'x86_64'}
# Not a dict
macs = 'aa:bb:cc:dd:ee:ff'
result = {'properties': properties, 'macs': macs}
ilo_mock.get_essential_properties.return_value = result
result = self.assertRaises(
exception.HardwareInspectionFailure,
ilo_inspect._get_essential_properties, self.node, ilo_mock)
def test__validate_ok(self):
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '2', 'cpu_arch': 'x86_arch'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa'}
data = {'properties': properties, 'macs': macs}
valid_keys = set(ilo_inspect.ESSENTIAL_PROPERTIES_KEYS)
ilo_inspect._validate(self.node, data)
self.assertEqual(sorted(set(properties)), sorted(valid_keys))
def test__validate_essential_keys_fail_missing_key(self):
properties = {'memory_mb': '512', 'local_gb': '10',
'cpus': '1'}
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa'}
data = {'properties': properties, 'macs': macs}
self.assertRaises(exception.HardwareInspectionFailure,
ilo_inspect._validate, self.node, data)
def test__get_macs_for_desired_ports(self):
driver_info_mock = {'inspect_ports': '1,2'}
self.node.driver_info = driver_info_mock
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
expected_macs = {'Port 1': 'aa:aa:aa:aa:aa:aa',
'Port 2': 'bb:bb:bb:bb:bb:bb'}
macs_out = (
ilo_inspect._get_macs_for_desired_ports(self.node,
macs))
self.assertEqual(expected_macs, macs_out)
def test__get_macs_for_desired_ports_few(self):
driver_info_mock = {'inspect_ports': '1,2'}
self.node.driver_info = driver_info_mock
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb',
'Port 3': 'cc:cc:cc:cc:cc:cc', 'Port 4': 'dd:dd:dd:dd:dd:dd'}
expected_macs = {'Port 1': 'aa:aa:aa:aa:aa:aa',
'Port 2': 'bb:bb:bb:bb:bb:bb'}
macs_out = (
ilo_inspect._get_macs_for_desired_ports(self.node,
macs))
self.assertEqual(expected_macs, macs_out)
def test__get_macs_for_desired_ports_one(self):
driver_info_mock = {'inspect_ports': '1'}
self.node.driver_info = driver_info_mock
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
expected_macs = {'Port 1': 'aa:aa:aa:aa:aa:aa'}
macs_out = (
ilo_inspect._get_macs_for_desired_ports(self.node,
macs))
self.assertEqual(expected_macs, macs_out)
def test__get_macs_for_desired_ports_none(self):
driver_info_mock = {}
self.node.driver_info = driver_info_mock
macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'}
self.assertRaises(exception.HardwareInspectionFailure,
ilo_inspect._get_macs_for_desired_ports,
self.node, macs)

View File

@ -25,7 +25,6 @@ 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
from ironic.tests.conductor import utils as mgr_utils
from ironic.tests.db import base as db_base
@ -156,8 +155,8 @@ class IloPowerTestCase(db_base.DbTestCase):
driver_info=driver_info)
def test_get_properties(self):
expected = ilo_common.COMMON_PROPERTIES
expected.update(ilo_deploy.COMMON_PROPERTIES)
expected = ilo_common.COMMON_PROPERTIES.copy()
expected.update(ilo_common.INSPECT_PROPERTIES)
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(expected, task.driver.get_properties())