ironic/ironic/drivers/modules/oneview/deploy_utils.py
Sinval Vieira 4483de30ba Add Dynamic Allocation feature for the OneView drivers
This change is about adding the ability to the OneView drivers of
dynamically allocate OneView resources to Ironic. The current
version of the drivers consider what we call "pre-allocation" of
nodes, meaning that when a node is registered in Ironic, even if
it is not in use, this resource is still reserved in OneView.
This change will prevent such situations by allocating OneView
resources only at boot time, allowing both systems to really
share the same pool of hardware.

Change-Id: I43d1db490b4834080562946b8a6ca584ea36864d
Co-Authored-By: Lilia Sampaio <liliars@lsd.ufcg.edu.br>
Co-Authored-By: Xavier <marcusrafael@lsd.ufcg.edu.br>
Co-Authored-By: Hugo Nicodemos <nicodemos@lsd.ufcg.edu.br>
Co-Authored-By: Thiago Paiva Brito <thiagop@lsd.ufcg.edu.br>
Co-Authored-By: Caio Oliveira <caiobo@lsd.ufcg.edu.br>
Partial-Bug: #1541096
2016-08-04 13:10:02 -03:00

336 lines
12 KiB
Python

# 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
)