Merge "Add inspection feature for the OneView drivers"
This commit is contained in:
commit
4858dd2e68
@ -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