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:
parent
7ea6b6ae11
commit
b78605487d
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
98
ironic/drivers/modules/oneview/inspect.py
Normal file
98
ironic/drivers/modules/oneview/inspect.py
Normal 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
|
@ -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)
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
95
ironic/tests/unit/drivers/modules/oneview/test_inspect.py
Normal file
95
ironic/tests/unit/drivers/modules/oneview/test_inspect.py
Normal 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)
|
@ -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(),
|
||||
|
@ -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:
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user