Add inspection feature for the OneView drivers

This change is about adding the ability to the OneView drivers of doing
hardware inspection. In this context, for enable hardware under
management by OneView been inspected, is essencial a Server Profile
applied to that hardware. A Server Profile, in OneView context, means
a configuration that makes booting and connectivity operations settings
possible.

Considering in-band inspection as the inspection process that involves
booting a ramdisk on the target node and fetching information directly
from it, in this patch we extend the
ironic.drivers.modules.inspector.Inspector class to inherit the ironic
implementation for in-band inspection behaviours and to address our
needs, we override the 'inspect_hardware' method to apply the needed
Server Profile and we also override the '_periodic_check_result'
periodic task to remove the Server Profile after the inspection
process is over.

Change-Id: I77ebf4b9175c2c1df1baa6d58714ff54c97c17ff
Closes-Bug: 1621530
This commit is contained in:
Xavier 2016-09-08 00:35:37 -03:00
parent 7ea6b6ae11
commit b78605487d
14 changed files with 458 additions and 60 deletions

View File

@ -8,7 +8,7 @@ proliantutils>=2.1.11
pyghmi>=0.8.0
pysnmp
python-ironic-inspector-client>=1.5.0
python-oneviewclient<3.0.0,>=2.0.2
python-oneviewclient<3.0.0,>=2.5.1
python-scciclient>=0.4.0
python-seamicroclient>=0.4.0
UcsSdk==0.8.2.2

View File

@ -354,3 +354,4 @@ class FakeOneViewDriver(base.BaseDriver):
self.management = oneview_management.OneViewManagement()
self.boot = fake.FakeBoot()
self.deploy = fake.FakeDeploy()
self.inspect = fake.FakeInspect()

View File

@ -155,13 +155,12 @@ def validate_oneview_resources_compatibility(task):
including server_hardware_uri, server_hardware_type_uri,
server_profile_template_uri, enclosure_group_uri and node ports. Also
verifies if a Server Profile is applied to the Server Hardware the node
represents. If any validation fails, python-oneviewclient will raise
an appropriate OneViewException.
represents when in pre-allocation model. If any validation fails,
python-oneviewclient will raise an appropriate OneViewException.
:param: task: a TaskManager instance containing the node to act on.
"""
node = task.node
node_ports = task.ports
oneview_info = get_oneview_info(task.node)
@ -169,13 +168,15 @@ def validate_oneview_resources_compatibility(task):
try:
oneview_client = get_oneview_client()
oneview_client.validate_node_server_hardware(
oneview_info, node.properties.get('memory_mb'),
node.properties.get('cpus')
)
oneview_client.validate_node_server_profile_template(oneview_info)
oneview_client.validate_node_server_hardware_type(oneview_info)
oneview_client.validate_node_enclosure_group(oneview_info)
oneview_client.validate_node_server_profile_template(oneview_info)
oneview_client.validate_node_server_hardware(
oneview_info,
task.node.properties.get('memory_mb'),
task.node.properties.get('cpus')
)
# NOTE(thiagop): Support to pre-allocation will be dropped in the Pike
# release
@ -183,12 +184,12 @@ def validate_oneview_resources_compatibility(task):
oneview_client.is_node_port_mac_compatible_with_server_hardware(
oneview_info, node_ports
)
oneview_client.validate_node_server_profile_template(oneview_info)
else:
oneview_client.check_server_profile_is_applied(oneview_info)
oneview_client.is_node_port_mac_compatible_with_server_profile(
oneview_info, node_ports
)
except oneview_exceptions.OneViewException as oneview_exc:
msg = (_("Error validating node resources with OneView: %s") %
oneview_exc)

View File

@ -211,6 +211,14 @@ class OneViewIscsiDeploy(iscsi_deploy.ISCSIDeploy, OneViewPeriodicTasks):
def get_properties(self):
deploy_utils.get_properties()
def validate(self, task):
common.verify_node_info(task.node)
try:
common.validate_oneview_resources_compatibility(task)
except exception.OneViewError as oneview_exc:
raise exception.InvalidParameterValue(oneview_exc)
super(OneViewIscsiDeploy, self).validate(task)
def prepare(self, task):
if common.is_dynamic_allocation_enabled(task.node):
deploy_utils.prepare(task)
@ -241,6 +249,14 @@ class OneViewAgentDeploy(agent.AgentDeploy, OneViewPeriodicTasks):
def get_properties(self):
deploy_utils.get_properties()
def validate(self, task):
common.verify_node_info(task.node)
try:
common.validate_oneview_resources_compatibility(task)
except exception.OneViewError as oneview_exc:
raise exception.InvalidParameterValue(oneview_exc)
super(OneViewAgentDeploy, self).validate(task)
def prepare(self, task):
if common.is_dynamic_allocation_enabled(task.node):
deploy_utils.prepare(task)

View File

@ -14,11 +14,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import operator
from oslo_log import log as logging
from oslo_utils import importutils
from ironic.common import exception
from ironic.common.i18n import _, _LE, _LI, _LW
from ironic.common.i18n import _, _LE, _LI
from ironic.common import states
from ironic.drivers.modules.oneview import common
@ -54,7 +56,7 @@ def prepare(task):
{"instance_name": instance_display_name,
"instance_uuid": instance_uuid}
)
_allocate_server_hardware_to_ironic(task.node, server_profile_name)
allocate_server_hardware_to_ironic(task.node, server_profile_name)
except exception.OneViewError as e:
raise exception.InstanceDeployFailure(node=task.node.uuid,
reason=e)
@ -74,7 +76,7 @@ def tear_down(task):
"""
try:
_deallocate_server_hardware_from_ironic(task.node)
deallocate_server_hardware_from_ironic(task.node)
except exception.OneViewError as e:
raise exception.InstanceDeployFailure(node=task.node.uuid, reason=e)
@ -94,7 +96,7 @@ def prepare_cleaning(task):
"""
try:
server_profile_name = "Ironic Cleaning [%s]" % task.node.uuid
_allocate_server_hardware_to_ironic(task.node, server_profile_name)
allocate_server_hardware_to_ironic(task.node, server_profile_name)
except exception.OneViewError as e:
oneview_error = common.SERVER_HARDWARE_ALLOCATION_ERROR
driver_internal_info = task.node.driver_internal_info
@ -119,11 +121,29 @@ def tear_down_cleaning(task):
"""
try:
_deallocate_server_hardware_from_ironic(task.node)
deallocate_server_hardware_from_ironic(task.node)
except exception.OneViewError as e:
raise exception.NodeCleaningFailure(node=task.node.uuid, reason=e)
def _is_node_in_use(server_hardware, applied_sp_uri, by_oneview=False):
"""Check if node is in use by ironic or by OneView.
:param by_oneview: Boolean value. True when want to verify if node is in
use by OneView. False to verify if node is in use by
ironic.
:param node: an ironic node object
:returns: Boolean value. True if by_oneview param is also True and node is
in use by OneView, False otherwise. True if by_oneview param is
False and node is in use by ironic, False otherwise.
"""
operation = operator.ne if by_oneview else operator.eq
return (server_hardware.server_profile_uri not in (None, '') and
operation(applied_sp_uri, server_hardware.server_profile_uri))
def is_node_in_use_by_oneview(node):
"""Check if node is in use by OneView user.
@ -131,6 +151,54 @@ def is_node_in_use_by_oneview(node):
:returns: Boolean value. True if node is in use by OneView,
False otherwise.
:raises OneViewError: if not possible to get OneView's informations
for the given node, if not possible to retrieve Server Hardware
from OneView.
"""
positive = _("Node '%s' is in use by OneView.") % node.uuid
negative = _("Node '%s' is not in use by OneView.") % node.uuid
def predicate(server_hardware, applied_sp_uri):
# Check if Profile exists in Oneview and it is different of the one
# applied by ironic
return _is_node_in_use(server_hardware, applied_sp_uri,
by_oneview=True)
return _check_applied_server_profile(node, predicate, positive, negative)
def is_node_in_use_by_ironic(node):
"""Check if node is in use by ironic in OneView.
:param node: an ironic node object
:returns: Boolean value. True if node is in use by ironic,
False otherwise.
:raises OneViewError: if not possible to get OneView's information
for the given node, if not possible to retrieve Server Hardware
from OneView.
"""
positive = _("Node '%s' is in use by Ironic.") % node.uuid
negative = _("Node '%s' is not in use by Ironic.") % node.uuid
def predicate(server_hardware, applied_sp_uri):
# Check if Profile exists in Oneview and it is equals of the one
# applied by ironic
return _is_node_in_use(server_hardware, applied_sp_uri,
by_oneview=False)
return _check_applied_server_profile(node, predicate, positive, negative)
def _check_applied_server_profile(node, predicate, positive, negative):
"""Check if node is in use by ironic in OneView.
:param node: an ironic node object
:returns: Boolean value. True if node is in use by ironic,
False otherwise.
:raises OneViewError: if not possible to get OneView's information
for the given node, if not possible to retrieve Server Hardware
from OneView.
@ -157,24 +225,14 @@ def is_node_in_use_by_oneview(node):
node.driver_info.get('applied_server_profile_uri')
)
# Check if Profile exists in Oneview and it is different of the one
# applied by ironic
if (server_hardware.server_profile_uri not in (None, '') and
applied_sp_uri != server_hardware.server_profile_uri):
LOG.warning(_LW("Node %s is already in use by OneView."),
node.uuid)
return True
result = predicate(server_hardware, applied_sp_uri)
if result:
LOG.debug(positive)
else:
LOG.debug(_(
"Hardware %(hardware_uri)s is free for use by "
"ironic on node %(node_uuid)s."),
{"hardware_uri": server_hardware.uri,
"node_uuid": node.uuid})
LOG.debug(negative)
return False
return result
def _add_applied_server_profile_uri_field(node, applied_profile):
@ -201,7 +259,7 @@ def _del_applied_server_profile_uri_field(node):
node.save()
def _allocate_server_hardware_to_ironic(node, server_profile_name):
def allocate_server_hardware_to_ironic(node, server_profile_name):
"""Allocate Server Hardware to ironic.
:param node: an ironic node object
@ -276,7 +334,7 @@ def _allocate_server_hardware_to_ironic(node, server_profile_name):
raise exception.OneViewError(error=msg)
def _deallocate_server_hardware_from_ironic(node):
def deallocate_server_hardware_from_ironic(node):
"""Deallocate Server Hardware from ironic.
:param node: an ironic node object

View File

@ -0,0 +1,98 @@
# Copyright 2016 Hewlett Packard Enterprise Development LP.
# Copyright 2016 Universidade Federal de Campina Grande
# All Rights Reserved.
#
# 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.
from futurist import periodics
from oslo_log import log as logging
from oslo_utils import importutils
from ironic.common import exception
from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers.modules import inspector
from ironic.drivers.modules.oneview import common
from ironic.drivers.modules.oneview import deploy_utils
from ironic.conf import CONF
LOG = logging.getLogger(__name__)
oneview_exception = importutils.try_import('oneview_client.exceptions')
oneview_utils = importutils.try_import('oneview_client.utils')
class OneViewInspect(inspector.Inspector):
"""Interface for in band inspection."""
def get_properties(self):
return common.COMMON_PROPERTIES
def validate(self, task):
"""Checks required info on 'driver_info' and validates node with OneView
Validates whether the 'driver_info' property of the supplied
task's node contains the required info such as server_hardware_uri,
server_hardware_type, server_profile_template_uri and
enclosure_group_uri. Also, checks if the server profile of the node is
applied, if NICs are valid for the server profile of the node.
:param task: a task from TaskManager.
:raises: InvalidParameterValue if parameters set are inconsistent with
resources in OneView
"""
common.verify_node_info(task.node)
try:
common.validate_oneview_resources_compatibility(task)
except exception.OneViewError as oneview_exc:
raise exception.InvalidParameterValue(oneview_exc)
def inspect_hardware(self, task):
profile_name = 'Ironic Inspecting [%s]' % task.node.uuid
deploy_utils.allocate_server_hardware_to_ironic(
task.node, profile_name
)
return super(OneViewInspect, self).inspect_hardware(task)
@periodics.periodic(spacing=CONF.inspector.status_check_period,
enabled=CONF.inspector.enabled)
def _periodic_check_result(self, manager, context):
filters = {'provision_state': states.INSPECTING}
node_iter = manager.iter_nodes(filters=filters)
for node_uuid, driver in node_iter:
if driver in [common.AGENT_PXE_ONEVIEW,
common.ISCSI_PXE_ONEVIEW]:
try:
lock_purpose = 'checking hardware inspection status'
with task_manager.acquire(context, node_uuid,
shared=True,
purpose=lock_purpose) as task:
self._check_status(task)
except (exception.NodeLocked, exception.NodeNotFound):
continue
def _check_status(self, task):
state_before = task.node.provision_state
result = inspector._check_status(task)
state_after = task.node.provision_state
# inspection finished
if (state_before == states.INSPECTING and
state_after in [states.MANAGEABLE, states.INSPECTFAIL]):
deploy_utils.deallocate_server_hardware_from_ironic(task.node)
return result

View File

@ -23,6 +23,7 @@ from ironic.common.i18n import _
from ironic.conductor import task_manager
from ironic.drivers import base
from ironic.drivers.modules.oneview import common
from ironic.drivers.modules.oneview import deploy_utils
LOG = logging.getLogger(__name__)
@ -65,6 +66,10 @@ class OneViewManagement(base.ManagementInterface):
try:
common.validate_oneview_resources_compatibility(task)
if not deploy_utils.is_node_in_use_by_ironic(task.node):
raise exception.InvalidParameterValue(
_("Node %s is not in use by ironic.") % task.node.uuid)
except exception.OneViewError as oneview_exc:
raise exception.InvalidParameterValue(oneview_exc)

View File

@ -24,6 +24,7 @@ from ironic.drivers import base
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers.modules.oneview import common
from ironic.drivers.modules.oneview import deploy
from ironic.drivers.modules.oneview import inspect
from ironic.drivers.modules.oneview import management
from ironic.drivers.modules.oneview import power
from ironic.drivers.modules.oneview import vendor
@ -55,6 +56,8 @@ class AgentPXEOneViewDriver(base.BaseDriver):
self.boot = pxe.PXEBoot()
self.deploy = deploy.OneViewAgentDeploy()
self.vendor = vendor.AgentVendorInterface()
self.inspect = inspect.OneViewInspect.create_if_enabled(
'AgentPXEOneViewDriver')
class ISCSIPXEOneViewDriver(base.BaseDriver):
@ -82,3 +85,5 @@ class ISCSIPXEOneViewDriver(base.BaseDriver):
self.boot = pxe.PXEBoot()
self.deploy = deploy.OneViewIscsiDeploy()
self.vendor = iscsi_deploy.VendorPassthru()
self.inspect = inspect.OneViewInspect.create_if_enabled(
'ISCSIPXEOneViewDriver')

View File

@ -237,8 +237,6 @@ class OneViewCommonTestCase(db_base.DbTestCase):
oneview_client = mock_get_ov_client()
with task_manager.acquire(self.context, self.node.uuid) as task:
common.validate_oneview_resources_compatibility(task)
self.assertTrue(
oneview_client.validate_node_server_hardware.called)
self.assertTrue(
oneview_client.validate_node_server_hardware_type.called)
self.assertTrue(
@ -264,15 +262,14 @@ class OneViewCommonTestCase(db_base.DbTestCase):
"""Validate compatibility of resources for Dynamic Allocation model.
1) Set 'dynamic_allocation' flag as True on node's driver_info
2) Check validate_node_server_hardware method is called
3) Check validate_node_server_hardware_type method is called
4) Check validate_node_enclosure_group method is called
5) Check validate_node_server_profile_template method is called
6) Check is_node_port_mac_compatible_with_server_hardware method
2) Check validate_node_server_hardware_type method is called
3) Check validate_node_enclosure_group method is called
4) Check validate_node_server_profile_template method is called
5) Check is_node_port_mac_compatible_with_server_hardware method
is called
7) Check validate_node_server_profile_template method is called
8) Check check_server_profile_is_applied method is not called
9) Check is_node_port_mac_compatible_with_server_profile method is
6) Check validate_node_server_profile_template method is called
7) Check check_server_profile_is_applied method is not called
8) Check is_node_port_mac_compatible_with_server_profile method is
not called
"""
@ -283,8 +280,6 @@ class OneViewCommonTestCase(db_base.DbTestCase):
task.node.driver_info = driver_info
common.validate_oneview_resources_compatibility(task)
self.assertTrue(
oneview_client.validate_node_server_hardware.called)
self.assertTrue(
oneview_client.validate_node_server_hardware_type.called)
self.assertTrue(

View File

@ -242,6 +242,43 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase):
deploy_utils.is_node_in_use_by_oneview(task.node)
)
# Tests for is_node_in_use_by_oneview
def test_is_node_in_use_by_ironic(self, mock_get_ov_client):
"""Node has a Server Profile applied by ironic.
"""
fake_sh = oneview_models.ServerHardware()
fake_sh.server_profile_uri = "same/applied_sp_uri/"
ov_client = mock_get_ov_client.return_value
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
with task_manager.acquire(self.context, self.node.uuid) as task:
driver_info = task.node.driver_info
driver_info['dynamic_allocation'] = True
driver_info['applied_server_profile_uri'] = 'same/applied_sp_uri/'
task.node.driver_info = driver_info
self.assertTrue(
deploy_utils.is_node_in_use_by_ironic(task.node)
)
def test_is_node_in_use_by_ironic_no_server_profile(
self, mock_get_ov_client
):
"""Node has no Server Profile.
"""
fake_sh = oneview_models.ServerHardware()
fake_sh.server_profile_uri = None
ov_client = mock_get_ov_client.return_value
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertFalse(
deploy_utils.is_node_in_use_by_ironic(task.node)
)
# Tests for _add_applied_server_profile_uri_field
def test__add_applied_server_profile_uri_field(self, mock_get_ov_client):
"""Checks if applied_server_profile_uri was added to driver_info.
@ -276,9 +313,9 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase):
self.assertNotIn('applied_server_profile_uri',
task.node.driver_info)
# Tests for _allocate_server_hardware_to_ironic
# Tests for allocate_server_hardware_to_ironic
@mock.patch.object(objects.Node, 'save')
def test__allocate_server_hardware_to_ironic(
def test_allocate_server_hardware_to_ironic(
self, mock_node_save, mock_get_ov_client
):
"""Checks if a Server Profile was created and its uri is in driver_info.
@ -291,7 +328,7 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase):
mock_get_ov_client.return_value = ov_client
with task_manager.acquire(self.context, self.node.uuid) as task:
deploy_utils._allocate_server_hardware_to_ironic(
deploy_utils.allocate_server_hardware_to_ironic(
task.node, 'serverProfileName'
)
self.assertTrue(ov_client.clone_template_and_apply.called)
@ -300,7 +337,7 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase):
@mock.patch.object(objects.Node, 'save')
@mock.patch.object(deploy_utils,
'_del_applied_server_profile_uri_field')
def test__allocate_server_hardware_to_ironic_node_has_server_profile(
def test_allocate_server_hardware_to_ironic_node_has_server_profile(
self, mock_delete_applied_sp, mock_node_save, mock_get_ov_client
):
"""Tests server profile allocation when applied_server_profile_uri exists.
@ -321,14 +358,14 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase):
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
task.node.driver_info = driver_info
deploy_utils._allocate_server_hardware_to_ironic(
deploy_utils.allocate_server_hardware_to_ironic(
task.node, 'serverProfileName'
)
self.assertTrue(mock_delete_applied_sp.called)
# Tests for _deallocate_server_hardware_from_ironic
# Tests for deallocate_server_hardware_from_ironic
@mock.patch.object(objects.Node, 'save')
def test__deallocate_server_hardware_from_ironic(
def test_deallocate_server_hardware_from_ironic(
self, mock_node_save, mock_get_ov_client
):
ov_client = mock_get_ov_client.return_value
@ -342,7 +379,7 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase):
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
task.node.driver_info = driver_info
deploy_utils._deallocate_server_hardware_from_ironic(task.node)
deploy_utils.deallocate_server_hardware_from_ironic(task.node)
self.assertTrue(ov_client.delete_server_profile.called)
self.assertTrue(
'applied_server_profile_uri' not in task.node.driver_info

View File

@ -0,0 +1,95 @@
# Copyright 2016 Hewlett Packard Enterprise Development LP.
# Copyright 2016 Universidade Federal de Campina Grande
# All Rights Reserved.
#
# 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.
import mock
from ironic.conductor import task_manager
from ironic.drivers.modules.oneview import common as oneview_common
from ironic.drivers.modules.oneview import deploy_utils
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.objects import utils as obj_utils
class AgentPXEOneViewInspectTestCase(db_base.DbTestCase):
def setUp(self):
super(AgentPXEOneViewInspectTestCase, self).setUp()
self.config(enabled=True, group='inspector')
mgr_utils.mock_the_extension_manager(driver="agent_pxe_oneview")
self.node = obj_utils.create_test_node(
self.context, driver='agent_pxe_oneview',
properties=db_utils.get_test_oneview_properties(),
driver_info=db_utils.get_test_oneview_driver_info(),
)
def test_get_properties(self):
expected = oneview_common.COMMON_PROPERTIES
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(expected, task.driver.inspect.get_properties())
@mock.patch.object(oneview_common, 'verify_node_info', spec_set=True,
autospec=True)
def test_validate(self, mock_verify_node_info):
self.config(enabled=False, group='inspector')
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.inspect.validate(task)
mock_verify_node_info.assert_called_once_with(task.node)
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic')
def test_inspect_hardware(self, mock_allocate_server_hardware_to_ironic):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.inspect.inspect_hardware(task)
self.assertTrue(mock_allocate_server_hardware_to_ironic.called)
class ISCSIPXEOneViewInspectTestCase(db_base.DbTestCase):
def setUp(self):
super(ISCSIPXEOneViewInspectTestCase, self).setUp()
self.config(enabled=True, group='inspector')
mgr_utils.mock_the_extension_manager(driver="iscsi_pxe_oneview")
self.node = obj_utils.create_test_node(
self.context, driver='iscsi_pxe_oneview',
properties=db_utils.get_test_oneview_properties(),
driver_info=db_utils.get_test_oneview_driver_info(),
)
def test_get_properties(self):
expected = oneview_common.COMMON_PROPERTIES
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(expected, task.driver.inspect.get_properties())
@mock.patch.object(oneview_common, 'verify_node_info', spec_set=True,
autospec=True)
def test_validate(self, mock_verify_node_info):
self.config(enabled=False, group='inspector')
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.inspect.validate(task)
mock_verify_node_info.assert_called_once_with(task.node)
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic')
def test_inspect_hardware(self, mock_allocate_server_hardware_to_ironic):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.inspect.inspect_hardware(task)
self.assertTrue(mock_allocate_server_hardware_to_ironic.called)

View File

@ -32,6 +32,7 @@ from ironic.tests.unit.objects import utils as obj_utils
oneview_exceptions = importutils.try_import('oneview_client.exceptions')
oneview_models = importutils.try_import('oneview_client.models')
@mock.patch.object(common, 'get_oneview_client', spect_set=True, autospec=True)
@ -56,10 +57,37 @@ class OneViewManagementDriverTestCase(db_base.DbTestCase):
@mock.patch.object(common, 'validate_oneview_resources_compatibility',
spect_set=True, autospec=True)
def test_validate(self, mock_validate, mock_get_ov_client):
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.driver.management.validate(task)
self.assertTrue(mock_validate.called)
@mock.patch.object(common, 'validate_oneview_resources_compatibility',
spect_set=True, autospec=True)
def test_validate_for_node_not_in_use_by_ironic(self,
mock_validate,
mock_get_ov_client):
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'other/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.validate, task)
def test_validate_fail(self, mock_get_ov_client):
node = obj_utils.create_test_node(self.context,
uuid=uuidutils.generate_uuid(),

View File

@ -15,16 +15,18 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
import time
import types
import mock
from oslo_utils import importutils
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 import agent_client
from ironic.drivers.modules.oneview import common
from ironic.drivers.modules.oneview import power
from ironic.drivers.modules.oneview import vendor
from ironic.drivers.modules import pxe
@ -34,10 +36,13 @@ from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.objects import utils as obj_utils
oneview_models = importutils.try_import('oneview_client.models')
GET_POWER_STATE_RETRIES = 5
@mock.patch.object(common, 'get_oneview_client', spec_set=True, autospec=True)
class TestBaseAgentVendor(db_base.DbTestCase):
def setUp(self):
@ -63,7 +68,8 @@ class TestBaseAgentVendor(db_base.DbTestCase):
@mock.patch('ironic.conductor.utils.node_set_boot_device', autospec=True)
def test_reboot_and_finish_deploy(self, set_bootdev_mock, power_off_mock,
get_power_state_mock,
node_power_action_mock):
node_power_action_mock,
mock_get_ov_client):
self.node.provision_state = states.DEPLOYING
self.node.target_provision_state = states.ACTIVE
self.node.save()
@ -89,9 +95,18 @@ class TestBaseAgentVendor(db_base.DbTestCase):
spec=types.FunctionType)
def test_reboot_and_finish_deploy_soft_poweroff_doesnt_complete(
self, power_off_mock, get_power_state_mock,
node_power_action_mock):
node_power_action_mock, mock_get_ov_client):
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
self.node.provision_state = states.DEPLOYING
self.node.target_provision_state = states.ACTIVE
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -111,10 +126,20 @@ class TestBaseAgentVendor(db_base.DbTestCase):
@mock.patch.object(agent_client.AgentClient, 'power_off',
spec=types.FunctionType)
def test_reboot_and_finish_deploy_soft_poweroff_fails(
self, power_off_mock, node_power_action_mock):
self, power_off_mock, node_power_action_mock,
mock_get_ov_client):
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
power_off_mock.side_effect = RuntimeError("boom")
self.node.provision_state = states.DEPLOYING
self.node.target_provision_state = states.ACTIVE
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -135,9 +160,18 @@ class TestBaseAgentVendor(db_base.DbTestCase):
spec=types.FunctionType)
def test_reboot_and_finish_deploy_get_power_state_fails(
self, power_off_mock, get_power_state_mock,
node_power_action_mock):
node_power_action_mock, mock_get_ov_client):
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
self.node.provision_state = states.DEPLOYING
self.node.target_provision_state = states.ACTIVE
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -162,7 +196,8 @@ class TestBaseAgentVendor(db_base.DbTestCase):
spec=types.FunctionType)
def test_reboot_and_finish_deploy_power_action_fails(
self, power_off_mock, get_power_state_mock,
node_power_action_mock, collect_ramdisk_logs_mock):
node_power_action_mock, collect_ramdisk_logs_mock,
mock_get_ov_client):
self.node.provision_state = states.DEPLOYING
self.node.target_provision_state = states.ACTIVE
self.node.save()
@ -193,11 +228,20 @@ class TestBaseAgentVendor(db_base.DbTestCase):
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
def test_reboot_to_instance(self, clean_pxe_mock, check_deploy_mock,
power_off_mock, get_power_state_mock,
node_power_action_mock):
node_power_action_mock, mock_get_ov_client):
check_deploy_mock.return_value = None
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
self.node.provision_state = states.DEPLOYWAIT
self.node.target_provision_state = states.ACTIVE
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -227,11 +271,21 @@ class TestBaseAgentVendor(db_base.DbTestCase):
check_deploy_mock,
power_off_mock,
get_power_state_mock,
node_power_action_mock):
node_power_action_mock,
mock_get_ov_client):
client = mock_get_ov_client.return_value
fake_server_hardware = oneview_models.ServerHardware()
fake_server_hardware.server_profile_uri = 'any/applied_sp_uri/'
client.get_server_hardware_by_uuid.return_value = fake_server_hardware
mock_get_ov_client.return_value = client
check_deploy_mock.return_value = None
self.node.provision_state = states.DEPLOYWAIT
self.node.target_provision_state = states.ACTIVE
driver_info = self.node.driver_info
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
self.node.driver_info = driver_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:

View File

@ -0,0 +1,5 @@
---
upgrade:
- Minimum required version of python-oneviewclient bumped to 2.5.1
features:
- Adds in-band inspection interface usable by OneView drivers.