Merge "iLO implementation for hardware inspection"
This commit is contained in:
commit
930f479dc1
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue