Merge "Add Dynamic Allocation feature for the OneView drivers"
This commit is contained in:
commit
1dc46d9e36
@ -83,7 +83,7 @@ IRONIC_HW_ARCH=${IRONIC_HW_ARCH:-x86_64}
|
||||
# <BMC address> <MAC address> <BMC username> <BMC password> <UCS service profile>
|
||||
#
|
||||
# *_oneview:
|
||||
# <Server Hardware URI> <Server Hardware Type URI> <Enclosure Group URI> <Server Profile Template URI> <MAC of primary connection>
|
||||
# <Server Hardware URI> <Server Hardware Type URI> <Enclosure Group URI> <Server Profile Template URI> <MAC of primary connection> <Applied Server Profile URI>
|
||||
#
|
||||
# IRONIC_IPMIINFO_FILE is deprecated, please use IRONIC_HWINFO_FILE. IRONIC_IPMIINFO_FILE will be removed in Ocata.
|
||||
IRONIC_IPMIINFO_FILE=${IRONIC_IPMIINFO_FILE:-""}
|
||||
@ -1053,8 +1053,11 @@ function enroll_nodes {
|
||||
local server_profile_template_uri
|
||||
server_profile_template_uri=$(echo $hardware_info |awk '{print $4}')
|
||||
mac_address=$(echo $hardware_info |awk '{print $5}')
|
||||
local applied_server_profile_uri
|
||||
applied_server_profile_uri=$(echo $hardware_info |awk '{print $6}')
|
||||
|
||||
node_options+=" -i server_hardware_uri=$server_hardware_uri"
|
||||
node_options+=" -i applied_server_profile_uri=$applied_server_profile_uri"
|
||||
node_options+=" -p capabilities="
|
||||
node_options+="server_hardware_type_uri:$server_hardware_type_uri,"
|
||||
node_options+="enclosure_group_uri:$enclosure_group_uri,"
|
||||
|
@ -1909,26 +1909,37 @@
|
||||
# From ironic
|
||||
#
|
||||
|
||||
# URL where OneView is available (string value)
|
||||
# URL where OneView is available. (string value)
|
||||
#manager_url = <None>
|
||||
|
||||
# OneView username to be used (string value)
|
||||
# OneView username to be used. (string value)
|
||||
#username = <None>
|
||||
|
||||
# OneView password to be used (string value)
|
||||
# OneView password to be used. (string value)
|
||||
#password = <None>
|
||||
|
||||
# Option to allow insecure connection with OneView (boolean
|
||||
# Option to allow insecure connection with OneView. (boolean
|
||||
# value)
|
||||
#allow_insecure_connections = false
|
||||
|
||||
# Path to CA certificate (string value)
|
||||
# Path to CA certificate. (string value)
|
||||
#tls_cacert_file = <None>
|
||||
|
||||
# Max connection retries to check changes on OneView (integer
|
||||
# Max connection retries to check changes on OneView. (integer
|
||||
# value)
|
||||
#max_polling_attempts = 12
|
||||
|
||||
# Period (in seconds) for periodic tasks to be executed.
|
||||
# (integer value)
|
||||
#periodic_check_interval = 300
|
||||
|
||||
# Whether to enable the periodic tasks for OneView driver be
|
||||
# aware when OneView hardware resources are taken and released
|
||||
# by Ironic or OneView users and proactively manage nodes in
|
||||
# clean fail state according to Dynamic Allocation model of
|
||||
# hardware resources allocation in OneView. (boolean value)
|
||||
#enable_periodic_tasks = true
|
||||
|
||||
|
||||
[oslo_concurrency]
|
||||
|
||||
|
@ -595,6 +595,11 @@ class OneViewError(IronicException):
|
||||
_msg_fmt = _("OneView exception occurred. Error: %(error)s")
|
||||
|
||||
|
||||
class OneViewInvalidNodeParameter(OneViewError):
|
||||
_msg_fmt = _("Error while obtaining OneView info from node %(node_uuid)s. "
|
||||
"Error: %(error)s")
|
||||
|
||||
|
||||
class NodeTagNotFound(IronicException):
|
||||
_msg_fmt = _("Node %(node_id)s doesn't have a tag '%(tag)s'")
|
||||
|
||||
|
@ -20,20 +20,32 @@ from ironic.common.i18n import _
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('manager_url',
|
||||
help=_('URL where OneView is available')),
|
||||
help=_('URL where OneView is available.')),
|
||||
cfg.StrOpt('username',
|
||||
help=_('OneView username to be used')),
|
||||
help=_('OneView username to be used.')),
|
||||
cfg.StrOpt('password',
|
||||
secret=True,
|
||||
help=_('OneView password to be used')),
|
||||
help=_('OneView password to be used.')),
|
||||
cfg.BoolOpt('allow_insecure_connections',
|
||||
default=False,
|
||||
help=_('Option to allow insecure connection with OneView')),
|
||||
help=_('Option to allow insecure connection with OneView.')),
|
||||
cfg.StrOpt('tls_cacert_file',
|
||||
help=_('Path to CA certificate')),
|
||||
help=_('Path to CA certificate.')),
|
||||
cfg.IntOpt('max_polling_attempts',
|
||||
default=12,
|
||||
help=_('Max connection retries to check changes on OneView')),
|
||||
help=_('Max connection retries to check changes on OneView.')),
|
||||
cfg.BoolOpt('enable_periodic_tasks',
|
||||
default=True,
|
||||
help=_('Whether to enable the periodic tasks for OneView '
|
||||
'driver be aware when OneView hardware resources are '
|
||||
'taken and released by Ironic or OneView users '
|
||||
'and proactively manage nodes in clean fail state '
|
||||
'according to Dynamic Allocation model of hardware '
|
||||
'resources allocation in OneView.')),
|
||||
cfg.IntOpt('periodic_check_interval',
|
||||
default=300,
|
||||
help=_('Period (in seconds) for periodic tasks to be '
|
||||
'executed when enable_periodic_tasks=True.')),
|
||||
]
|
||||
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
#
|
||||
# Copyright 2015 Hewlett Packard Development Company, LP
|
||||
# Copyright 2015 Universidade Federal de Campina Grande
|
||||
#
|
||||
@ -61,6 +60,15 @@ COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO)
|
||||
COMMON_PROPERTIES.update(REQUIRED_ON_PROPERTIES)
|
||||
COMMON_PROPERTIES.update(OPTIONAL_ON_PROPERTIES)
|
||||
|
||||
ISCSI_PXE_ONEVIEW = 'iscsi_pxe_oneview'
|
||||
AGENT_PXE_ONEVIEW = 'agent_pxe_oneview'
|
||||
|
||||
# NOTE(xavierr): We don't want to translate NODE_IN_USE_BY_ONEVIEW and
|
||||
# SERVER_HARDWARE_ALLOCATION_ERROR to avoid inconsistency in the nodes
|
||||
# caused by updates on translation in upgrades of ironic.
|
||||
NODE_IN_USE_BY_ONEVIEW = 'node in use by OneView'
|
||||
SERVER_HARDWARE_ALLOCATION_ERROR = 'server hardware allocation error'
|
||||
|
||||
|
||||
def get_oneview_client():
|
||||
"""Generates an instance of the OneView client.
|
||||
@ -70,7 +78,6 @@ def get_oneview_client():
|
||||
|
||||
:returns: an instance of the OneView client
|
||||
"""
|
||||
|
||||
oneview_client = client.Client(
|
||||
manager_url=CONF.oneview.manager_url,
|
||||
username=CONF.oneview.username,
|
||||
@ -140,12 +147,16 @@ def get_oneview_info(node):
|
||||
:enclosure_group_uri: the uri of the enclosure group in OneView
|
||||
:server_profile_template_uri: the uri of the server profile template in
|
||||
OneView
|
||||
:raises InvalidParameterValue if node capabilities are malformed
|
||||
:raises OneViewInvalidNodeParameter if node capabilities are malformed
|
||||
"""
|
||||
|
||||
capabilities_dict = utils.capabilities_to_dict(
|
||||
node.properties.get('capabilities', '')
|
||||
)
|
||||
try:
|
||||
capabilities_dict = utils.capabilities_to_dict(
|
||||
node.properties.get('capabilities', '')
|
||||
)
|
||||
except exception.InvalidParameterValue as e:
|
||||
raise exception.OneViewInvalidNodeParameter(node_uuid=node.uuid,
|
||||
error=e)
|
||||
|
||||
driver_info = node.driver_info
|
||||
|
||||
@ -159,6 +170,8 @@ def get_oneview_info(node):
|
||||
'server_profile_template_uri':
|
||||
capabilities_dict.get('server_profile_template_uri') or
|
||||
driver_info.get('server_profile_template_uri'),
|
||||
'applied_server_profile_uri':
|
||||
driver_info.get('applied_server_profile_uri'),
|
||||
}
|
||||
|
||||
return oneview_info
|
||||
@ -180,25 +193,41 @@ def validate_oneview_resources_compatibility(task):
|
||||
|
||||
node = task.node
|
||||
node_ports = task.ports
|
||||
|
||||
try:
|
||||
oneview_info = get_oneview_info(task.node)
|
||||
except exception.InvalidParameterValue as e:
|
||||
msg = (_("Error while obtaining OneView info from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
try:
|
||||
oneview_client = get_oneview_client()
|
||||
oneview_info = get_oneview_info(node)
|
||||
|
||||
oneview_client.validate_node_server_hardware(
|
||||
oneview_info, node.properties.get('memory_mb'),
|
||||
node.properties.get('cpus')
|
||||
)
|
||||
oneview_client.validate_node_server_hardware_type(oneview_info)
|
||||
oneview_client.check_server_profile_is_applied(oneview_info)
|
||||
oneview_client.is_node_port_mac_compatible_with_server_profile(
|
||||
oneview_info, node_ports
|
||||
)
|
||||
oneview_client.validate_node_enclosure_group(oneview_info)
|
||||
oneview_client.validate_node_server_profile_template(oneview_info)
|
||||
|
||||
# NOTE(thiagop): Support to pre-allocation will be dropped in 'P'
|
||||
# release
|
||||
if is_dynamic_allocation_enabled(task.node):
|
||||
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)
|
||||
LOG.error(msg)
|
||||
msg = (_("Error validating node resources with OneView: %s") %
|
||||
oneview_exc)
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
|
||||
@ -252,7 +281,13 @@ def node_has_server_profile(func):
|
||||
"""
|
||||
def inner(*args, **kwargs):
|
||||
task = args[1]
|
||||
oneview_info = get_oneview_info(task.node)
|
||||
try:
|
||||
oneview_info = get_oneview_info(task.node)
|
||||
except exception.InvalidParameterValue as e:
|
||||
msg = (_("Error while obtaining OneView info from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': task.node.uuid, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
oneview_client = get_oneview_client()
|
||||
try:
|
||||
node_has_server_profile = (
|
||||
@ -272,3 +307,17 @@ def node_has_server_profile(func):
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
return inner
|
||||
|
||||
|
||||
def is_dynamic_allocation_enabled(node):
|
||||
flag = node.driver_info.get('dynamic_allocation')
|
||||
if flag:
|
||||
if isinstance(flag, bool):
|
||||
return flag is True
|
||||
else:
|
||||
msg = (_LE("Invalid dynamic_allocation parameter value in "
|
||||
"node's %(node_uuid)s driver_info. Valid values "
|
||||
"are booleans true or false.") %
|
||||
{"node_uuid": node.uuid})
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
return False
|
||||
|
264
ironic/drivers/modules/oneview/deploy.py
Normal file
264
ironic/drivers/modules/oneview/deploy.py
Normal file
@ -0,0 +1,264 @@
|
||||
# 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 abc
|
||||
|
||||
from futurist import periodics
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common import states
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
from ironic import objects
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONF = common.CONF
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class OneViewPeriodicTasks(object):
|
||||
|
||||
@abc.abstractproperty
|
||||
def oneview_driver(self):
|
||||
pass
|
||||
|
||||
@periodics.periodic(spacing=CONF.oneview.periodic_check_interval,
|
||||
enabled=CONF.oneview.enable_periodic_tasks)
|
||||
def _periodic_check_nodes_taken_by_oneview(self, manager, context):
|
||||
"""Checks if nodes in Ironic were taken by OneView users.
|
||||
|
||||
This driver periodic task will check for nodes that were taken by
|
||||
OneView users while the node is in available state, set the node to
|
||||
maintenance mode with an appropriate maintenance reason message and
|
||||
move the node to manageable state.
|
||||
|
||||
:param manager: a ConductorManager instance
|
||||
:param context: request context
|
||||
:returns: None.
|
||||
"""
|
||||
|
||||
filters = {
|
||||
'provision_state': states.AVAILABLE,
|
||||
'maintenance': False,
|
||||
'driver': self.oneview_driver
|
||||
}
|
||||
node_iter = manager.iter_nodes(filters=filters)
|
||||
|
||||
for node_uuid, driver in node_iter:
|
||||
|
||||
node = objects.Node.get(context, node_uuid)
|
||||
|
||||
try:
|
||||
oneview_using = deploy_utils.is_node_in_use_by_oneview(node)
|
||||
except exception.OneViewError as e:
|
||||
LOG.error(_LE("Error while determining if node "
|
||||
"%(node_uuid)s is in use by OneView. "
|
||||
"Error: %(error)s"),
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
|
||||
if oneview_using:
|
||||
purpose = (_LI('Updating node %(node_uuid)s in use '
|
||||
'by OneView from %(provision_state)s state '
|
||||
'to %(target_state)s state and maintenance '
|
||||
'mode %(maintenance)s.'),
|
||||
{'node_uuid': node_uuid,
|
||||
'provision_state': states.AVAILABLE,
|
||||
'target_state': states.MANAGEABLE,
|
||||
'maintenance': True})
|
||||
|
||||
LOG.info(purpose)
|
||||
|
||||
node.maintenance = True
|
||||
node.maintenance_reason = common.NODE_IN_USE_BY_ONEVIEW
|
||||
manager.update_node(context, node)
|
||||
manager.do_provisioning_action(context, node.uuid, 'manage')
|
||||
|
||||
@periodics.periodic(spacing=CONF.oneview.periodic_check_interval,
|
||||
enabled=CONF.oneview.enable_periodic_tasks)
|
||||
def _periodic_check_nodes_freed_by_oneview(self, manager, context):
|
||||
"""Checks if nodes taken by OneView users were freed.
|
||||
|
||||
This driver periodic task will be responsible to poll the nodes that
|
||||
are in maintenance mode and on manageable state to check if the Server
|
||||
Profile was removed, indicating that the node was freed by the OneView
|
||||
user. If so, it'll provide the node, that will pass through the
|
||||
cleaning process and become available to be provisioned.
|
||||
|
||||
:param manager: a ConductorManager instance
|
||||
:param context: request context
|
||||
:returns: None.
|
||||
"""
|
||||
|
||||
filters = {
|
||||
'provision_state': states.MANAGEABLE,
|
||||
'maintenance': True,
|
||||
'driver': self.oneview_driver
|
||||
}
|
||||
node_iter = manager.iter_nodes(fields=['maintenance_reason'],
|
||||
filters=filters)
|
||||
for node_uuid, driver, maintenance_reason in node_iter:
|
||||
|
||||
if maintenance_reason == common.NODE_IN_USE_BY_ONEVIEW:
|
||||
|
||||
node = objects.Node.get(context, node_uuid)
|
||||
|
||||
try:
|
||||
oneview_using = deploy_utils.is_node_in_use_by_oneview(
|
||||
node
|
||||
)
|
||||
except exception.OneViewError as e:
|
||||
LOG.error(_LE("Error while determining if node "
|
||||
"%(node_uuid)s is in use by OneView. "
|
||||
"Error: %(error)s"),
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
|
||||
if not oneview_using:
|
||||
purpose = (_LI('Bringing node %(node_uuid)s back from '
|
||||
'use by OneView from %(provision_state)s '
|
||||
'state to %(target_state)s state and '
|
||||
'maintenance mode %(maintenance)s.'),
|
||||
{'node_uuid': node_uuid,
|
||||
'provision_state': states.MANAGEABLE,
|
||||
'target_state': states.AVAILABLE,
|
||||
'maintenance': False})
|
||||
|
||||
LOG.info(purpose)
|
||||
|
||||
node.maintenance = False
|
||||
node.maintenance_reason = None
|
||||
manager.update_node(context, node)
|
||||
manager.do_provisioning_action(
|
||||
context, node.uuid, 'provide'
|
||||
)
|
||||
|
||||
@periodics.periodic(spacing=CONF.oneview.periodic_check_interval,
|
||||
enabled=CONF.oneview.enable_periodic_tasks)
|
||||
def _periodic_check_nodes_taken_on_cleanfail(self, manager, context):
|
||||
"""Checks failed deploys due to Oneview users taking Server Hardware.
|
||||
|
||||
This last driver periodic task will take care of nodes that would be
|
||||
caught on a race condition between OneView and a deploy by Ironic. In
|
||||
such cases, the validation will fail, throwing the node on deploy fail
|
||||
and, afterwards on clean fail.
|
||||
|
||||
This task will set the node to maintenance mode with a proper reason
|
||||
message and move it to manageable state, from where the second task
|
||||
can rescue the node as soon as the Server Profile is removed.
|
||||
|
||||
:param manager: a ConductorManager instance
|
||||
:param context: request context
|
||||
:returns: None.
|
||||
"""
|
||||
|
||||
filters = {
|
||||
'provision_state': states.CLEANFAIL,
|
||||
'driver': self.oneview_driver
|
||||
}
|
||||
node_iter = manager.iter_nodes(fields=['driver_internal_info'],
|
||||
filters=filters)
|
||||
|
||||
for node_uuid, driver, driver_internal_info in node_iter:
|
||||
|
||||
node_oneview_error = driver_internal_info.get('oneview_error')
|
||||
if node_oneview_error == common.SERVER_HARDWARE_ALLOCATION_ERROR:
|
||||
|
||||
node = objects.Node.get(context, node_uuid)
|
||||
|
||||
purpose = (_LI('Bringing node %(node_uuid)s back from use '
|
||||
'by OneView from %(provision_state)s state '
|
||||
'to %(target_state)s state and '
|
||||
'maintenance mode %(maintenance)s.'),
|
||||
{'node_uuid': node_uuid,
|
||||
'provision_state': states.CLEANFAIL,
|
||||
'target_state': states.MANAGEABLE,
|
||||
'maintenance': False})
|
||||
|
||||
LOG.info(purpose)
|
||||
|
||||
node.maintenance = True
|
||||
node.maintenance_reason = common.NODE_IN_USE_BY_ONEVIEW
|
||||
driver_internal_info = node.driver_internal_info
|
||||
driver_internal_info.pop('oneview_error', None)
|
||||
node.driver_internal_info = driver_internal_info
|
||||
manager.update_node(context, node)
|
||||
manager.do_provisioning_action(context, node.uuid, 'manage')
|
||||
|
||||
|
||||
class OneViewIscsiDeploy(iscsi_deploy.ISCSIDeploy, OneViewPeriodicTasks):
|
||||
"""Class for OneView ISCSI deployment driver."""
|
||||
|
||||
oneview_driver = common.ISCSI_PXE_ONEVIEW
|
||||
|
||||
def get_properties(self):
|
||||
deploy_utils.get_properties()
|
||||
|
||||
def prepare(self, task):
|
||||
if common.is_dynamic_allocation_enabled(task.node):
|
||||
deploy_utils.prepare(task)
|
||||
super(OneViewIscsiDeploy, self).prepare(task)
|
||||
|
||||
def tear_down(self, task):
|
||||
if (common.is_dynamic_allocation_enabled(task.node) and
|
||||
not CONF.conductor.automated_clean):
|
||||
deploy_utils.tear_down(task)
|
||||
super(OneViewIscsiDeploy, self).tear_down(task)
|
||||
|
||||
def prepare_cleaning(self, task):
|
||||
if common.is_dynamic_allocation_enabled(task.node):
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
return super(OneViewIscsiDeploy, self).prepare_cleaning(task)
|
||||
|
||||
def tear_down_cleaning(self, task):
|
||||
if common.is_dynamic_allocation_enabled(task.node):
|
||||
deploy_utils.tear_down_cleaning(task)
|
||||
return super(OneViewIscsiDeploy, self).tear_down_cleaning(task)
|
||||
|
||||
|
||||
class OneViewAgentDeploy(agent.AgentDeploy, OneViewPeriodicTasks):
|
||||
"""Class for OneView Agent deployment driver."""
|
||||
|
||||
oneview_driver = common.AGENT_PXE_ONEVIEW
|
||||
|
||||
def get_properties(self):
|
||||
deploy_utils.get_properties()
|
||||
|
||||
def prepare(self, task):
|
||||
if common.is_dynamic_allocation_enabled(task.node):
|
||||
deploy_utils.prepare(task)
|
||||
super(OneViewAgentDeploy, self).prepare(task)
|
||||
|
||||
def tear_down(self, task):
|
||||
if (common.is_dynamic_allocation_enabled(task.node) and
|
||||
not CONF.conductor.automated_clean):
|
||||
deploy_utils.tear_down(task)
|
||||
super(OneViewAgentDeploy, self).tear_down(task)
|
||||
|
||||
def prepare_cleaning(self, task):
|
||||
if common.is_dynamic_allocation_enabled(task.node):
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
return super(OneViewAgentDeploy, self).prepare_cleaning(task)
|
||||
|
||||
def tear_down_cleaning(self, task):
|
||||
if common.is_dynamic_allocation_enabled(task.node):
|
||||
deploy_utils.tear_down_cleaning(task)
|
||||
return super(OneViewAgentDeploy, self).tear_down_cleaning(task)
|
335
ironic/drivers/modules/oneview/deploy_utils.py
Normal file
335
ironic/drivers/modules/oneview/deploy_utils.py
Normal file
@ -0,0 +1,335 @@
|
||||
# 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 oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common.i18n import _LW
|
||||
from ironic.common import states
|
||||
from ironic.drivers.modules.oneview import common
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
oneview_exception = importutils.try_import('oneview_client.exceptions')
|
||||
oneview_utils = importutils.try_import('oneview_client.utils')
|
||||
|
||||
|
||||
def get_properties():
|
||||
return common.COMMON_PROPERTIES
|
||||
|
||||
|
||||
def prepare(task):
|
||||
"""Applies Server Profile and update the node when preparing.
|
||||
|
||||
This method is responsible for applying a Server Profile to the Server
|
||||
Hardware and add the uri of the applied Server Profile in the node's
|
||||
'applied_server_profile_uri' field on properties/capabilities.
|
||||
|
||||
:param task: A TaskManager object
|
||||
:raises InstanceDeployFailure: If the node doesn't have the needed OneView
|
||||
informations, if Server Hardware is in use by an OneView user, or
|
||||
if the Server Profile can't be applied.
|
||||
|
||||
"""
|
||||
if task.node.provision_state == states.DEPLOYING:
|
||||
try:
|
||||
instance_display_name = task.node.instance_info.get('display_name')
|
||||
instance_uuid = task.node.instance_uuid
|
||||
server_profile_name = (
|
||||
"%(instance_name)s [%(instance_uuid)s]" %
|
||||
{"instance_name": instance_display_name,
|
||||
"instance_uuid": instance_uuid}
|
||||
)
|
||||
_allocate_server_hardware_to_ironic(task.node, server_profile_name)
|
||||
except exception.OneViewError as e:
|
||||
raise exception.InstanceDeployFailure(node=task.node.uuid,
|
||||
reason=e)
|
||||
|
||||
|
||||
def tear_down(task):
|
||||
"""Remove Server profile and update the node when tear down.
|
||||
|
||||
This method is responsible for power a Server Hardware off, remove a Server
|
||||
Profile from the Server Hardware and remove the uri of the applied Server
|
||||
Profile from the node's 'applied_server_profile_uri' in
|
||||
properties/capabilities.
|
||||
|
||||
:param task: A TaskManager object
|
||||
:raises InstanceDeployFailure: If node has no uri of applied Server
|
||||
Profile, or if some error occur while deleting Server Profile.
|
||||
|
||||
"""
|
||||
try:
|
||||
_deallocate_server_hardware_from_ironic(task.node)
|
||||
except exception.OneViewError as e:
|
||||
raise exception.InstanceDeployFailure(node=task.node.uuid, reason=e)
|
||||
|
||||
|
||||
def prepare_cleaning(task):
|
||||
"""Applies Server Profile and update the node when preparing cleaning.
|
||||
|
||||
This method is responsible for applying a Server Profile to the Server
|
||||
Hardware and add the uri of the applied Server Profile in the node's
|
||||
'applied_server_profile_uri' field on properties/capabilities.
|
||||
|
||||
:param task: A TaskManager object
|
||||
:raises NodeCleaningFailure: If the node doesn't have the needed OneView
|
||||
informations, if Server Hardware is in use by an OneView user, or
|
||||
if the Server Profile can't be applied.
|
||||
|
||||
"""
|
||||
try:
|
||||
server_profile_name = "Ironic Cleaning [%s]" % task.node.uuid
|
||||
_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
|
||||
driver_internal_info['oneview_error'] = oneview_error
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
raise exception.NodeCleaningFailure(node=task.node.uuid,
|
||||
reason=e)
|
||||
|
||||
|
||||
def tear_down_cleaning(task):
|
||||
"""Remove Server profile and update the node when tear down cleaning.
|
||||
|
||||
This method is responsible for power a Server Hardware off, remove a Server
|
||||
Profile from the Server Hardware and remove the uri of the applied Server
|
||||
Profile from the node's 'applied_server_profile_uri' in
|
||||
properties/capabilities.
|
||||
|
||||
:param task: A TaskManager object
|
||||
:raises NodeCleaningFailure: If node has no uri of applied Server Profile,
|
||||
or if some error occur while deleting Server Profile.
|
||||
|
||||
"""
|
||||
try:
|
||||
_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_by_oneview(node):
|
||||
"""Check if node is in use by OneView user.
|
||||
|
||||
:param node: an ironic node object
|
||||
: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.
|
||||
|
||||
"""
|
||||
try:
|
||||
oneview_info = common.get_oneview_info(node)
|
||||
except exception.InvalidParameterValue as e:
|
||||
msg = (_("Error while obtaining OneView info from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
oneview_client = common.get_oneview_client()
|
||||
|
||||
sh_uuid = oneview_utils.get_uuid_from_uri(
|
||||
oneview_info.get("server_hardware_uri")
|
||||
)
|
||||
|
||||
try:
|
||||
server_hardware = oneview_client.get_server_hardware_by_uuid(
|
||||
sh_uuid
|
||||
)
|
||||
except oneview_exception.OneViewResourceNotFoundError as e:
|
||||
msg = (_("Error while obtaining Server Hardware from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
applied_sp_uri = (
|
||||
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
|
||||
|
||||
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})
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _add_applied_server_profile_uri_field(node, applied_profile):
|
||||
"""Adds the applied Server Profile uri to a node.
|
||||
|
||||
:param node: an ironic node object
|
||||
|
||||
"""
|
||||
driver_info = node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = applied_profile.uri
|
||||
node.driver_info = driver_info
|
||||
node.save()
|
||||
|
||||
|
||||
def _del_applied_server_profile_uri_field(node):
|
||||
"""Delete the applied Server Profile uri from a node if it exists.
|
||||
|
||||
:param node: an ironic node object
|
||||
|
||||
"""
|
||||
driver_info = node.driver_info
|
||||
driver_info.pop('applied_server_profile_uri', None)
|
||||
node.driver_info = driver_info
|
||||
node.save()
|
||||
|
||||
|
||||
def _allocate_server_hardware_to_ironic(node, server_profile_name):
|
||||
"""Allocate Server Hardware to ironic.
|
||||
|
||||
:param node: an ironic node object
|
||||
:param server_profile_name: a formatted string with the Server Profile
|
||||
name
|
||||
:raises OneViewError: if an error occurs while allocating the Server
|
||||
Hardware to ironic
|
||||
|
||||
"""
|
||||
node_in_use_by_oneview = is_node_in_use_by_oneview(node)
|
||||
|
||||
if not node_in_use_by_oneview:
|
||||
|
||||
try:
|
||||
oneview_info = common.get_oneview_info(node)
|
||||
except exception.InvalidParameterValue as e:
|
||||
msg = (_("Error while obtaining OneView info from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
applied_sp_uri = node.driver_info.get('applied_server_profile_uri')
|
||||
|
||||
sh_uuid = oneview_utils.get_uuid_from_uri(
|
||||
oneview_info.get("server_hardware_uri")
|
||||
)
|
||||
spt_uuid = oneview_utils.get_uuid_from_uri(
|
||||
oneview_info.get("server_profile_template_uri")
|
||||
)
|
||||
oneview_client = common.get_oneview_client()
|
||||
server_hardware = oneview_client.get_server_hardware_by_uuid(sh_uuid)
|
||||
|
||||
# Don't have Server Profile on OneView but has
|
||||
# `applied_server_profile_uri` on driver_info
|
||||
if (server_hardware.server_profile_uri in (None, '') and
|
||||
applied_sp_uri is not (None, '')):
|
||||
|
||||
_del_applied_server_profile_uri_field(node)
|
||||
LOG.info(_LI(
|
||||
"Inconsistent 'applied_server_profile_uri' parameter "
|
||||
"value in driver_info. There is no Server Profile "
|
||||
"applied to node %(node_uuid)s. Value deleted."),
|
||||
{"node_uuid": node.uuid}
|
||||
)
|
||||
|
||||
# applied_server_profile_uri exists and is equal to Server profile
|
||||
# applied on Hardware. Do not apply again.
|
||||
if (applied_sp_uri and server_hardware.server_profile_uri and
|
||||
server_hardware.server_profile_uri == applied_sp_uri):
|
||||
LOG.info(_LI(
|
||||
"The Server Profile %(applied_sp_uri)s was already applied "
|
||||
"by ironic on node %(node_uuid)s. Reusing."),
|
||||
{"node_uuid": node.uuid, "applied_sp_uri": applied_sp_uri}
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
applied_profile = oneview_client.clone_template_and_apply(
|
||||
server_profile_name, sh_uuid, spt_uuid
|
||||
)
|
||||
_add_applied_server_profile_uri_field(node, applied_profile)
|
||||
|
||||
LOG.info(
|
||||
_LI("Server Profile %(server_profile_uuid)s was successfully"
|
||||
" applied to node %(node_uuid)s."),
|
||||
{"node_uuid": node.uuid,
|
||||
"server_profile_uuid": applied_profile.uri}
|
||||
)
|
||||
|
||||
except oneview_exception.OneViewServerProfileAssignmentError as e:
|
||||
LOG.error(_LE("An error occurred during allocating server "
|
||||
"hardware to ironic during prepare: %s"), e)
|
||||
raise exception.OneViewError(error=e)
|
||||
else:
|
||||
msg = (_("Node %s is already in use by OneView.") %
|
||||
node.uuid)
|
||||
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
|
||||
def _deallocate_server_hardware_from_ironic(node):
|
||||
"""Deallocate Server Hardware from ironic.
|
||||
|
||||
:param node: an ironic node object
|
||||
:raises OneViewError: if an error occurs while deallocating the Server
|
||||
Hardware to ironic
|
||||
|
||||
"""
|
||||
try:
|
||||
oneview_info = common.get_oneview_info(node)
|
||||
except exception.InvalidParameterValue as e:
|
||||
msg = (_("Error while obtaining OneView info from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
oneview_client = common.get_oneview_client()
|
||||
oneview_client.power_off(oneview_info)
|
||||
|
||||
applied_sp_uuid = oneview_utils.get_uuid_from_uri(
|
||||
oneview_info.get('applied_server_profile_uri')
|
||||
)
|
||||
|
||||
try:
|
||||
oneview_client.delete_server_profile(applied_sp_uuid)
|
||||
_del_applied_server_profile_uri_field(node)
|
||||
|
||||
LOG.info(
|
||||
_LI("Server Profile %(server_profile_uuid)s was successfully"
|
||||
" deleted from node %(node_uuid)s."
|
||||
),
|
||||
{"node_uuid": node.uuid, "server_profile_uuid": applied_sp_uuid}
|
||||
)
|
||||
except oneview_exception.OneViewException as e:
|
||||
|
||||
msg = (_("Error while deleting applied Server Profile from node "
|
||||
"%(node_uuid)s. Error: %(error)s") %
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
|
||||
raise exception.OneViewError(
|
||||
node=node.uuid, reason=msg
|
||||
)
|
@ -97,7 +97,6 @@ class OneViewManagement(base.ManagementInterface):
|
||||
if the server is already powered on.
|
||||
:raises: OneViewError if the communication with OneView fails
|
||||
"""
|
||||
|
||||
oneview_info = common.get_oneview_info(task.node)
|
||||
|
||||
if device not in self.get_supported_boot_devices(task):
|
||||
@ -115,7 +114,6 @@ class OneViewManagement(base.ManagementInterface):
|
||||
"Error setting boot device on OneView. Error: %s")
|
||||
% oneview_exc
|
||||
)
|
||||
LOG.error(msg)
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
@common.node_has_server_profile
|
||||
@ -135,7 +133,6 @@ class OneViewManagement(base.ManagementInterface):
|
||||
:raises: InvalidParameterValue if the boot device is unknown
|
||||
:raises: OneViewError if the communication with OneView fails
|
||||
"""
|
||||
|
||||
oneview_info = common.get_oneview_info(task.node)
|
||||
|
||||
try:
|
||||
@ -146,7 +143,6 @@ class OneViewManagement(base.ManagementInterface):
|
||||
"Error getting boot device from OneView. Error: %s")
|
||||
% oneview_exc
|
||||
)
|
||||
LOG.error(msg)
|
||||
raise exception.OneViewError(msg)
|
||||
|
||||
primary_device = boot_order[0]
|
||||
|
@ -69,8 +69,8 @@ class OneViewPower(base.PowerInterface):
|
||||
:raises: OneViewError if fails to retrieve power state of OneView
|
||||
resource
|
||||
"""
|
||||
|
||||
oneview_info = common.get_oneview_info(task.node)
|
||||
|
||||
oneview_client = common.get_oneview_client()
|
||||
try:
|
||||
power_state = oneview_client.get_node_power_state(oneview_info)
|
||||
@ -95,8 +95,8 @@ class OneViewPower(base.PowerInterface):
|
||||
:raises: PowerStateFailure if the power couldn't be set to power_state.
|
||||
:raises: OneViewError if OneView fails setting the power state.
|
||||
"""
|
||||
|
||||
oneview_info = common.get_oneview_info(task.node)
|
||||
|
||||
oneview_client = common.get_oneview_client()
|
||||
|
||||
LOG.debug('Setting power state of node %(node_uuid)s to '
|
||||
|
@ -1,4 +1,3 @@
|
||||
#
|
||||
# Copyright 2015 Hewlett Packard Development Company, LP
|
||||
# Copyright 2015 Universidade Federal de Campina Grande
|
||||
#
|
||||
@ -22,9 +21,9 @@ from oslo_utils import importutils
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import agent
|
||||
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 management
|
||||
from ironic.drivers.modules.oneview import power
|
||||
from ironic.drivers.modules.oneview import vendor
|
||||
@ -32,14 +31,12 @@ from ironic.drivers.modules import pxe
|
||||
|
||||
|
||||
class AgentPXEOneViewDriver(base.BaseDriver):
|
||||
"""Agent + OneView driver.
|
||||
"""OneViewDriver using OneViewClient interface.
|
||||
|
||||
This driver implements the `core` functionality, combining
|
||||
:class:`ironic.drivers.ov.OVPower` for power on/off and reboot of virtual
|
||||
machines, with :class:`ironic.driver.pxe.PXEBoot` for booting deploy kernel
|
||||
and ramdisk and :class:`ironic.driver.iscsi_deploy.ISCSIDeploy` for image
|
||||
deployment. Implementations are in those respective classes; this class is
|
||||
merely the glue between them.
|
||||
This driver implements the `core` functionality using
|
||||
:class:ironic.drivers.modules.oneview.power.OneViewPower for power
|
||||
management. And
|
||||
:class:ironic.drivers.modules.oneview.deploy.OneViewAgentDeploy for deploy.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@ -56,19 +53,17 @@ class AgentPXEOneViewDriver(base.BaseDriver):
|
||||
self.power = power.OneViewPower()
|
||||
self.management = management.OneViewManagement()
|
||||
self.boot = pxe.PXEBoot()
|
||||
self.deploy = agent.AgentDeploy()
|
||||
self.deploy = deploy.OneViewAgentDeploy()
|
||||
self.vendor = vendor.AgentVendorInterface()
|
||||
|
||||
|
||||
class ISCSIPXEOneViewDriver(base.BaseDriver):
|
||||
"""PXE + OneView driver.
|
||||
"""OneViewDriver using OneViewClient interface.
|
||||
|
||||
This driver implements the `core` functionality, combining
|
||||
:class:`ironic.drivers.ov.OVPower` for power on/off and reboot of virtual
|
||||
machines, with :class:`ironic.driver.pxe.PXEBoot` for booting deploy kernel
|
||||
and ramdisk and :class:`ironic.driver.iscsi_deploy.ISCSIDeploy` for image
|
||||
deployment. Implementations are in those respective classes; this class is
|
||||
merely the glue between them.
|
||||
This driver implements the `core` functionality using
|
||||
:class:ironic.drivers.modules.oneview.power.OneViewPower for power
|
||||
management. And
|
||||
:class:ironic.drivers.modules.oneview.deploy.OneViewIscsiDeploy for deploy.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@ -85,5 +80,5 @@ class ISCSIPXEOneViewDriver(base.BaseDriver):
|
||||
self.power = power.OneViewPower()
|
||||
self.management = management.OneViewManagement()
|
||||
self.boot = pxe.PXEBoot()
|
||||
self.deploy = iscsi_deploy.ISCSIDeploy()
|
||||
self.deploy = deploy.OneViewIscsiDeploy()
|
||||
self.vendor = iscsi_deploy.VendorPassthru()
|
||||
|
@ -1,5 +1,3 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Hewlett Packard Development Company, LP
|
||||
# Copyright 2015 Universidade Federal de Campina Grande
|
||||
#
|
||||
@ -115,6 +113,7 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
'server_hardware_type_uri': 'fake_sht_uri',
|
||||
'enclosure_group_uri': 'fake_eg_uri',
|
||||
'server_profile_template_uri': 'fake_spt_uri',
|
||||
'applied_server_profile_uri': None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@ -124,7 +123,6 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
|
||||
def test_get_oneview_info_missing_spt(self):
|
||||
driver_info = db_utils.get_test_oneview_driver_info()
|
||||
|
||||
properties = db_utils.get_test_oneview_properties()
|
||||
properties["capabilities"] = ("server_hardware_type_uri:fake_sht_uri,"
|
||||
"enclosure_group_uri:fake_eg_uri")
|
||||
@ -138,6 +136,7 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
'server_hardware_type_uri': 'fake_sht_uri',
|
||||
'enclosure_group_uri': 'fake_eg_uri',
|
||||
'server_profile_template_uri': None,
|
||||
'applied_server_profile_uri': None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@ -165,6 +164,7 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
'server_hardware_type_uri': 'fake_sht_uri',
|
||||
'enclosure_group_uri': 'fake_eg_uri',
|
||||
'server_profile_template_uri': 'fake_spt_uri',
|
||||
'applied_server_profile_uri': None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@ -172,6 +172,20 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
common.get_oneview_info(incomplete_node)
|
||||
)
|
||||
|
||||
def test_get_oneview_info_malformed_capabilities(self):
|
||||
driver_info = db_utils.get_test_oneview_driver_info()
|
||||
|
||||
del driver_info["server_hardware_uri"]
|
||||
properties = db_utils.get_test_oneview_properties()
|
||||
properties["capabilities"] = "anything,000"
|
||||
|
||||
self.node.driver_info = driver_info
|
||||
self.node.properties = properties
|
||||
|
||||
self.assertRaises(exception.OneViewInvalidNodeParameter,
|
||||
common.get_oneview_info,
|
||||
self.node)
|
||||
|
||||
# TODO(gabriel-bezerra): Remove this after Mitaka
|
||||
@mock.patch.object(common, 'LOG', autospec=True)
|
||||
def test_deprecated_spt_in_driver_info(self, log_mock):
|
||||
@ -194,6 +208,7 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
'server_hardware_type_uri': 'fake_sht_uri',
|
||||
'enclosure_group_uri': 'fake_eg_uri',
|
||||
'server_profile_template_uri': 'fake_spt_uri',
|
||||
'applied_server_profile_uri': None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@ -226,6 +241,7 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
'server_hardware_type_uri': 'fake_sht_uri',
|
||||
'enclosure_group_uri': 'fake_eg_uri',
|
||||
'server_profile_template_uri': 'fake_spt_uri',
|
||||
'applied_server_profile_uri': None,
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
@ -281,8 +297,9 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
|
||||
@mock.patch.object(common, 'get_oneview_client', spec_set=True,
|
||||
autospec=True)
|
||||
def test_validate_oneview_resources_compatibility(self,
|
||||
mock_get_ov_client):
|
||||
def test_validate_oneview_resources_compatibility(
|
||||
self, mock_get_ov_client
|
||||
):
|
||||
oneview_client = mock_get_ov_client()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
common.validate_oneview_resources_compatibility(task)
|
||||
@ -290,12 +307,123 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
||||
oneview_client.validate_node_server_hardware.called)
|
||||
self.assertTrue(
|
||||
oneview_client.validate_node_server_hardware_type.called)
|
||||
self.assertTrue(
|
||||
oneview_client.validate_node_enclosure_group.called)
|
||||
self.assertTrue(
|
||||
oneview_client.validate_node_server_profile_template.called)
|
||||
self.assertTrue(
|
||||
oneview_client.check_server_profile_is_applied.called)
|
||||
self.assertTrue(
|
||||
oneview_client.is_node_port_mac_compatible_with_server_profile.
|
||||
called)
|
||||
oneview_client.
|
||||
is_node_port_mac_compatible_with_server_profile.called)
|
||||
self.assertFalse(
|
||||
oneview_client.
|
||||
is_node_port_mac_compatible_with_server_hardware.called)
|
||||
self.assertFalse(
|
||||
oneview_client.validate_spt_primary_boot_connection.called)
|
||||
|
||||
@mock.patch.object(common, 'get_oneview_client', spec_set=True,
|
||||
autospec=True)
|
||||
def test_validate_oneview_resources_compatibility_dynamic_allocation(
|
||||
self, mock_get_ov_client
|
||||
):
|
||||
"""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
|
||||
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
|
||||
not called
|
||||
|
||||
"""
|
||||
oneview_client = mock_get_ov_client()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = True
|
||||
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(
|
||||
oneview_client.validate_node_enclosure_group.called)
|
||||
self.assertTrue(
|
||||
oneview_client.validate_node_server_profile_template.called)
|
||||
self.assertTrue(
|
||||
oneview_client.
|
||||
is_node_port_mac_compatible_with_server_hardware.called)
|
||||
self.assertTrue(
|
||||
oneview_client.validate_node_server_profile_template.called)
|
||||
self.assertFalse(
|
||||
oneview_client.check_server_profile_is_applied.called)
|
||||
self.assertFalse(
|
||||
oneview_client.
|
||||
is_node_port_mac_compatible_with_server_profile.called)
|
||||
|
||||
def test_is_dynamic_allocation_enabled(self):
|
||||
"""Ensure Dynamic Allocation is enabled when flag is True.
|
||||
|
||||
1) Set 'dynamic_allocation' flag as True on node's driver_info
|
||||
2) Check Dynamic Allocation is enabled for the given node
|
||||
|
||||
"""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = True
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
self.assertTrue(
|
||||
common.is_dynamic_allocation_enabled(task.node)
|
||||
)
|
||||
|
||||
def test_is_dynamic_allocation_enabled_false(self):
|
||||
"""Ensure Dynamic Allocation is disabled when flag is False.
|
||||
|
||||
1) Set 'dynamic_allocation' flag as False on node's driver_info
|
||||
2) Check Dynamic Allocation is disabled for the given node
|
||||
|
||||
"""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = False
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
self.assertFalse(
|
||||
common.is_dynamic_allocation_enabled(task.node)
|
||||
)
|
||||
|
||||
def test_is_dynamic_allocation_enabled_none(self):
|
||||
"""Ensure Dynamic Allocation is disabled when flag is None.
|
||||
|
||||
1) Set 'dynamic_allocation' flag as None on node's driver_info
|
||||
2) Check Dynamic Allocation is disabled for the given node
|
||||
|
||||
"""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = None
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
self.assertFalse(
|
||||
common.is_dynamic_allocation_enabled(task.node)
|
||||
)
|
||||
|
||||
def test_is_dynamic_allocation_enabled_without_flag(self):
|
||||
"""Ensure Dynamic Allocation is disabled when node doesnt't have flag.
|
||||
|
||||
1) Create a node without 'dynamic_allocation' flag
|
||||
2) Check Dynamic Allocation is disabled for the given node
|
||||
|
||||
"""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertFalse(
|
||||
common.is_dynamic_allocation_enabled(task.node)
|
||||
)
|
||||
|
144
ironic/tests/unit/drivers/modules/oneview/test_deploy.py
Normal file
144
ironic/tests/unit/drivers/modules/oneview/test_deploy.py
Normal file
@ -0,0 +1,144 @@
|
||||
# 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 oslo_utils import importutils
|
||||
|
||||
from ironic.common import driver_factory
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
from ironic import objects
|
||||
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
|
||||
|
||||
oneview_models = importutils.try_import('oneview_client.models')
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_oneview_client', spec_set=True, autospec=True)
|
||||
class OneViewPeriodicTasks(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewPeriodicTasks, self).setUp()
|
||||
self.config(manager_url='https://1.2.3.4', group='oneview')
|
||||
self.config(username='user', group='oneview')
|
||||
self.config(password='password', group='oneview')
|
||||
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_oneview')
|
||||
self.driver = driver_factory.get_driver('fake_oneview')
|
||||
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_oneview',
|
||||
properties=db_utils.get_test_oneview_properties(),
|
||||
driver_info=db_utils.get_test_oneview_driver_info(),
|
||||
)
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
|
||||
@mock.patch.object(objects.Node, 'get')
|
||||
@mock.patch.object(deploy_utils, 'is_node_in_use_by_oneview')
|
||||
def test__periodic_check_nodes_taken_by_oneview(
|
||||
self, mock_is_node_in_use_by_oneview, mock_get_node,
|
||||
mock_get_ov_client
|
||||
):
|
||||
|
||||
manager = mock.MagicMock(
|
||||
spec=['iter_nodes', 'update_node', 'do_provisioning_action']
|
||||
)
|
||||
|
||||
manager.iter_nodes.return_value = [
|
||||
(self.node.uuid, 'fake_oneview')
|
||||
]
|
||||
|
||||
mock_get_node.return_value = self.node
|
||||
mock_is_node_in_use_by_oneview.return_value = True
|
||||
|
||||
class OneViewDriverDeploy(deploy.OneViewPeriodicTasks):
|
||||
oneview_driver = 'fake_oneview'
|
||||
|
||||
oneview_driver_deploy = OneViewDriverDeploy()
|
||||
oneview_driver_deploy._periodic_check_nodes_taken_by_oneview(
|
||||
manager, self.context
|
||||
)
|
||||
self.assertTrue(manager.update_node.called)
|
||||
self.assertTrue(manager.do_provisioning_action.called)
|
||||
self.assertTrue(self.node.maintenance)
|
||||
self.assertEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'is_node_in_use_by_oneview')
|
||||
def test__periodic_check_nodes_freed_by_oneview(
|
||||
self, mock_is_node_in_use_by_oneview, mock_get_ov_client
|
||||
):
|
||||
|
||||
manager = mock.MagicMock(
|
||||
spec=['iter_nodes', 'update_node', 'do_provisioning_action']
|
||||
)
|
||||
|
||||
manager.iter_nodes.return_value = [
|
||||
(self.node.uuid, 'fake_oneview',
|
||||
common.NODE_IN_USE_BY_ONEVIEW)
|
||||
]
|
||||
|
||||
mock_is_node_in_use_by_oneview.return_value = False
|
||||
|
||||
class OneViewDriverDeploy(deploy.OneViewPeriodicTasks):
|
||||
oneview_driver = 'fake_oneview'
|
||||
|
||||
oneview_driver_deploy = OneViewDriverDeploy()
|
||||
oneview_driver_deploy._periodic_check_nodes_freed_by_oneview(
|
||||
manager, self.context
|
||||
)
|
||||
self.assertTrue(manager.update_node.called)
|
||||
self.assertTrue(manager.do_provisioning_action.called)
|
||||
self.assertFalse(self.node.maintenance)
|
||||
self.assertIsNone(self.node.maintenance_reason)
|
||||
|
||||
@mock.patch.object(objects.Node, 'get')
|
||||
def test__periodic_check_nodes_taken_on_cleanfail(
|
||||
self, mock_get_node, mock_get_ov_client
|
||||
):
|
||||
|
||||
driver_internal_info = {
|
||||
'oneview_error': common.SERVER_HARDWARE_ALLOCATION_ERROR
|
||||
}
|
||||
|
||||
manager = mock.MagicMock(
|
||||
spec=['iter_nodes', 'update_node', 'do_provisioning_action']
|
||||
)
|
||||
|
||||
manager.iter_nodes.return_value = [
|
||||
(self.node.uuid, 'fake_oneview', driver_internal_info)
|
||||
]
|
||||
|
||||
self.node.driver_internal_info = driver_internal_info
|
||||
mock_get_node.return_value = self.node
|
||||
|
||||
class OneViewDriverDeploy(deploy.OneViewPeriodicTasks):
|
||||
oneview_driver = 'fake_oneview'
|
||||
|
||||
oneview_driver_deploy = OneViewDriverDeploy()
|
||||
oneview_driver_deploy._periodic_check_nodes_taken_on_cleanfail(
|
||||
manager, self.context
|
||||
)
|
||||
self.assertTrue(manager.update_node.called)
|
||||
self.assertTrue(manager.do_provisioning_action.called)
|
||||
self.assertTrue(self.node.maintenance)
|
||||
self.assertEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
self.assertDictEqual({}, self.node.driver_internal_info)
|
349
ironic/tests/unit/drivers/modules/oneview/test_deploy_utils.py
Normal file
349
ironic/tests/unit/drivers/modules/oneview/test_deploy_utils.py
Normal file
@ -0,0 +1,349 @@
|
||||
# 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 oslo_utils import importutils
|
||||
|
||||
from ironic.common import driver_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
from ironic import objects
|
||||
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
|
||||
|
||||
oneview_models = importutils.try_import('oneview_client.models')
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_oneview_client', spec_set=True, autospec=True)
|
||||
class OneViewDeployUtilsTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewDeployUtilsTestCase, self).setUp()
|
||||
self.config(manager_url='https://1.2.3.4', group='oneview')
|
||||
self.config(username='user', group='oneview')
|
||||
self.config(password='password', group='oneview')
|
||||
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_oneview')
|
||||
self.driver = driver_factory.get_driver('fake_oneview')
|
||||
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_oneview',
|
||||
properties=db_utils.get_test_oneview_properties(),
|
||||
driver_info=db_utils.get_test_oneview_driver_info(),
|
||||
)
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
|
||||
# Tests for prepare
|
||||
def test_prepare_node_is_in_use_by_oneview(self, mock_get_ov_client):
|
||||
"""`prepare` behavior when the node already has a Profile on OneView.
|
||||
|
||||
"""
|
||||
oneview_client = mock_get_ov_client()
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = "/any/sp_uri"
|
||||
oneview_client.get_server_hardware.return_value = fake_server_hardware
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = True
|
||||
task.node.driver_info = driver_info
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
self.assertRaises(
|
||||
exception.InstanceDeployFailure,
|
||||
deploy_utils.prepare,
|
||||
task
|
||||
)
|
||||
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test_prepare_node_is_successfuly_allocated_to_ironic(
|
||||
self, mock_node_save, mock_get_ov_client
|
||||
):
|
||||
"""`prepare` behavior when the node is free from OneView standpoint.
|
||||
|
||||
"""
|
||||
ov_client = mock_get_ov_client()
|
||||
fake_sh = oneview_models.ServerHardware()
|
||||
fake_sh.server_profile_uri = None
|
||||
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
deploy_utils.prepare(task)
|
||||
self.assertTrue(ov_client.clone_template_and_apply.called)
|
||||
self.assertTrue(ov_client.get_server_profile_from_hardware)
|
||||
|
||||
# Tests for tear_down
|
||||
def test_tear_down(self, mock_get_ov_client):
|
||||
"""`tear_down` behavior when node already has Profile applied
|
||||
|
||||
"""
|
||||
ov_client = mock_get_ov_client()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = \
|
||||
'/rest/server-profiles/1234556789'
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
self.assertTrue(
|
||||
'applied_server_profile_uri' in task.node.driver_info
|
||||
)
|
||||
deploy_utils.tear_down(task)
|
||||
self.assertFalse(
|
||||
'applied_server_profile_uri' in task.node.driver_info
|
||||
)
|
||||
self.assertTrue(
|
||||
ov_client.delete_server_profile.called
|
||||
)
|
||||
|
||||
# Tests for prepare_cleaning
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test_prepare_cleaning_when_node_does_not_have_sp_applied(
|
||||
self, mock_node_save, mock_get_ov_client
|
||||
):
|
||||
"""`prepare_cleaning` behavior when node is free
|
||||
|
||||
"""
|
||||
ov_client = mock_get_ov_client()
|
||||
fake_sh = oneview_models.ServerHardware()
|
||||
fake_sh.server_profile_uri = None
|
||||
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
self.assertTrue(ov_client.clone_template_and_apply.called)
|
||||
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test_prepare_cleaning_when_node_has_sp_applied(
|
||||
self, mock_node_save, mock_get_ov_client
|
||||
):
|
||||
"""`prepare_cleaning` behavior when node already has Profile applied
|
||||
|
||||
"""
|
||||
ov_client = mock_get_ov_client()
|
||||
fake_sh = oneview_models.ServerHardware()
|
||||
fake_sh.server_profile_uri = 'same/sp_applied'
|
||||
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['applied_server_profile_uri'] = 'same/sp_applied'
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
self.assertFalse(ov_client.clone_template_and_apply.called)
|
||||
|
||||
def test_prepare_cleaning_node_is_in_use_by_oneview(
|
||||
self, mock_get_ov_client
|
||||
):
|
||||
"""`prepare_cleaning` behavior when node has Server Profile on OneView
|
||||
|
||||
"""
|
||||
oneview_client = mock_get_ov_client()
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = "/any/sp_uri"
|
||||
oneview_client.get_server_hardware.return_value = fake_server_hardware
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = True
|
||||
task.node.driver_info = driver_info
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
self.assertRaises(
|
||||
exception.NodeCleaningFailure,
|
||||
deploy_utils.prepare_cleaning,
|
||||
task
|
||||
)
|
||||
|
||||
# Tests for tear_down_cleaning
|
||||
def test_tear_down_cleaning(self, mock_get_ov_client):
|
||||
"""Checks if Server Profile was deleted and its uri removed
|
||||
|
||||
"""
|
||||
ov_client = mock_get_ov_client()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = \
|
||||
'/rest/server-profiles/1234556789'
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
self.assertIn('applied_server_profile_uri', task.node.driver_info)
|
||||
deploy_utils.tear_down_cleaning(task)
|
||||
self.assertNotIn('applied_server_profile_uri',
|
||||
task.node.driver_info)
|
||||
self.assertTrue(ov_client.delete_server_profile.called)
|
||||
|
||||
# Tests for is_node_in_use_by_oneview
|
||||
def test_is_node_in_use_by_oneview(self, mock_get_ov_client):
|
||||
"""Node has a Server Profile applied by a third party user.
|
||||
|
||||
"""
|
||||
fake_server_hardware = oneview_models.ServerHardware()
|
||||
fake_server_hardware.server_profile_uri = "/any/sp_uri"
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['dynamic_allocation'] = True
|
||||
task.node.driver_info = driver_info
|
||||
self.assertTrue(
|
||||
deploy_utils.is_node_in_use_by_oneview(task.node)
|
||||
)
|
||||
|
||||
def test_is_node_in_use_by_oneview_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_oneview(task.node)
|
||||
)
|
||||
|
||||
def test_is_node_in_use_by_oneview_same_server_profile_applied(
|
||||
self, mock_get_ov_client
|
||||
):
|
||||
"""Node's Server Profile uri is the same 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['applied_server_profile_uri'] = 'same/applied_sp_uri/'
|
||||
task.node.driver_info = driver_info
|
||||
self.assertFalse(
|
||||
deploy_utils.is_node_in_use_by_oneview(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.
|
||||
|
||||
"""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
task.node.driver_info = driver_info
|
||||
fake_server_profile = oneview_models.ServerProfile()
|
||||
fake_server_profile.uri = 'any/applied_sp_uri/'
|
||||
|
||||
self.assertNotIn('applied_server_profile_uri',
|
||||
task.node.driver_info)
|
||||
deploy_utils._add_applied_server_profile_uri_field(
|
||||
task.node,
|
||||
fake_server_profile
|
||||
)
|
||||
self.assertIn('applied_server_profile_uri', task.node.driver_info)
|
||||
|
||||
# Tests for _del_applied_server_profile_uri_field
|
||||
def test__del_applied_server_profile_uri_field(self, mock_get_ov_client):
|
||||
"""Checks if applied_server_profile_uri was removed from driver_info.
|
||||
|
||||
"""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
self.assertIn('applied_server_profile_uri', task.node.driver_info)
|
||||
deploy_utils._del_applied_server_profile_uri_field(task.node)
|
||||
self.assertNotIn('applied_server_profile_uri',
|
||||
task.node.driver_info)
|
||||
|
||||
# Tests for _allocate_server_hardware_to_ironic
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
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.
|
||||
|
||||
"""
|
||||
ov_client = mock_get_ov_client.return_value
|
||||
fake_sh = oneview_models.ServerHardware()
|
||||
fake_sh.server_profile_uri = None
|
||||
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
|
||||
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(
|
||||
task.node, 'serverProfileName'
|
||||
)
|
||||
self.assertTrue(ov_client.clone_template_and_apply.called)
|
||||
self.assertIn('applied_server_profile_uri', task.node.driver_info)
|
||||
|
||||
@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(
|
||||
self, mock_delete_applied_sp, mock_node_save, mock_get_ov_client
|
||||
):
|
||||
"""Tests server profile allocation when applied_server_profile_uri exists.
|
||||
|
||||
This test consider that no Server Profile is applied on the Server
|
||||
Hardware but the applied_server_profile_uri remained on the node. Thus,
|
||||
the conductor should remove the value and apply a new server profile to
|
||||
use the node.
|
||||
"""
|
||||
ov_client = mock_get_ov_client.return_value
|
||||
fake_sh = oneview_models.ServerHardware()
|
||||
fake_sh.server_profile_uri = None
|
||||
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
|
||||
mock_get_ov_client.return_value = ov_client
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = 'any/applied_sp_uri/'
|
||||
task.node.driver_info = driver_info
|
||||
|
||||
deploy_utils._allocate_server_hardware_to_ironic(
|
||||
task.node, 'serverProfileName'
|
||||
)
|
||||
self.assertTrue(mock_delete_applied_sp.called)
|
||||
|
||||
# Tests for _deallocate_server_hardware_from_ironic
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test__deallocate_server_hardware_from_ironic(
|
||||
self, mock_node_save, mock_get_ov_client
|
||||
):
|
||||
ov_client = mock_get_ov_client.return_value
|
||||
fake_sh = oneview_models.ServerHardware()
|
||||
fake_sh.server_profile_uri = 'any/applied_sp_uri/'
|
||||
ov_client.get_server_hardware_by_uuid.return_value = fake_sh
|
||||
mock_get_ov_client.return_value = ov_client
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
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)
|
||||
self.assertTrue(ov_client.delete_server_profile.called)
|
||||
self.assertTrue(
|
||||
'applied_server_profile_uri' not in task.node.driver_info
|
||||
)
|
@ -126,6 +126,8 @@ ONEVIEWCLIENT_SPEC = (
|
||||
'client',
|
||||
'states',
|
||||
'exceptions',
|
||||
'models',
|
||||
'utils',
|
||||
)
|
||||
|
||||
ONEVIEWCLIENT_CLIENT_CLS_SPEC = (
|
||||
|
@ -126,8 +126,10 @@ if not oneview_client:
|
||||
ONEVIEW_ERROR='error')
|
||||
sys.modules['oneview_client.states'] = states
|
||||
sys.modules['oneview_client.exceptions'] = oneview_client.exceptions
|
||||
sys.modules['oneview_client.utils'] = oneview_client.utils
|
||||
oneview_client.exceptions.OneViewException = type('OneViewException',
|
||||
(Exception,), {})
|
||||
sys.modules['oneview_client.models'] = oneview_client.models
|
||||
if 'ironic.drivers.oneview' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview'])
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- Add Dynamic Allocation feature for the OneView drivers.
|
||||
deprecations:
|
||||
- Deprecates pre-allocation feature for the OneView drivers.
|
Loading…
Reference in New Issue
Block a user