Merge "Remove oneview drivers"
This commit is contained in:
commit
01d46be1fa
@ -113,9 +113,6 @@ IRONIC_HW_ARCH=${IRONIC_HW_ARCH:-x86_64}
|
||||
# cisco-ucs-managed:
|
||||
# <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> <Applied Server Profile URI>
|
||||
#
|
||||
# idrac:
|
||||
# <BMC address> <MAC address> <BMC username> <BMC password>
|
||||
#
|
||||
@ -288,7 +285,7 @@ fi
|
||||
# are ``ipmi``, ``snmp`` and ``redfish``.
|
||||
#
|
||||
# Additional valid choices if IRONIC_IS_HARDWARE == true are:
|
||||
# ``cisco-ucs-managed``, ``cisco-ucs-standalone``, ``oneview``, ``idrac``,
|
||||
# ``cisco-ucs-managed``, ``cisco-ucs-standalone``, ``idrac``,
|
||||
# and ``irmc``.
|
||||
IRONIC_DEPLOY_DRIVER=${IRONIC_DEPLOY_DRIVER:-ipmi}
|
||||
|
||||
@ -627,11 +624,6 @@ function is_deployed_by_ucs {
|
||||
return 1
|
||||
}
|
||||
|
||||
function is_deployed_by_oneview {
|
||||
[[ "${IRONIC_DEPLOY_DRIVER}" == oneview ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
function is_deployed_by_ilo {
|
||||
[[ "${IRONIC_DEPLOY_DRIVER}" == ilo ]] && return 0
|
||||
return 1
|
||||
@ -1936,32 +1928,6 @@ function enroll_nodes {
|
||||
--driver-info ucs_password=$bmc_passwd \
|
||||
--driver-info ucs_username=$bmc_username \
|
||||
--driver-info ucs_service_profile=$ucs_service_profile"
|
||||
elif is_deployed_by_oneview; then
|
||||
local server_hardware_uri
|
||||
server_hardware_uri=$(echo $hardware_info |awk '{print $1}')
|
||||
local server_hardware_type_uri
|
||||
server_hardware_type_uri=$(echo $hardware_info |awk '{print $2}')
|
||||
local enclosure_group_uri
|
||||
enclosure_group_uri=$(echo $hardware_info |awk '{print $3}')
|
||||
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+=" --driver-info server_hardware_uri=$server_hardware_uri"
|
||||
if [[ -n "$applied_server_profile_uri" ]]; then
|
||||
node_options+=" --driver-info applied_server_profile_uri=$applied_server_profile_uri"
|
||||
fi
|
||||
|
||||
if [[ "$node_capabilities" ]]; then
|
||||
node_capabilities+=","
|
||||
else
|
||||
node_capabilities+=" --property capabilities="
|
||||
fi
|
||||
node_capabilities+="server_hardware_type_uri:$server_hardware_type_uri,"
|
||||
node_capabilities+="enclosure_group_uri:$enclosure_group_uri,"
|
||||
node_capabilities+="server_profile_template_uri:$server_profile_template_uri"
|
||||
elif is_deployed_by_ilo; then
|
||||
node_options+=" --driver-info ilo_address=$bmc_address \
|
||||
--driver-info ilo_password=$bmc_passwd \
|
||||
|
@ -22,7 +22,6 @@ Hardware Types
|
||||
drivers/ilo
|
||||
drivers/ipmitool
|
||||
drivers/irmc
|
||||
drivers/oneview
|
||||
drivers/redfish
|
||||
drivers/snmp
|
||||
drivers/ucs
|
||||
|
@ -1,343 +0,0 @@
|
||||
.. _oneview:
|
||||
|
||||
==============
|
||||
OneView driver
|
||||
==============
|
||||
|
||||
.. note::
|
||||
The `oneview` hardware type, along with related interfaces to support
|
||||
OneView, have been deprecated, and should be expected to be
|
||||
removed from ironic in the Stein cycle. Please see
|
||||
`storyboard <https://storyboard.openstack.org/#!/story/2001924>`_ for
|
||||
additional details.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
HP OneView [1]_ is a single integrated platform, packaged as an appliance that
|
||||
implements a software-defined approach to managing physical infrastructure.
|
||||
The appliance supports scenarios such as deploying bare metal servers, for
|
||||
instance. In this context, the ``HP OneView driver`` for ironic enables the
|
||||
users of OneView to use ironic as a bare metal provider to their managed
|
||||
physical hardware.
|
||||
|
||||
HPE OneView hardware is supported by the ``oneview`` hardware type.
|
||||
|
||||
To provide a bare metal instance there are four components involved in the
|
||||
process:
|
||||
|
||||
* The ironic service
|
||||
* The ironic-inspector service (if using hardware inspection)
|
||||
* The ironic hardware type for OneView
|
||||
* The hpOneView library
|
||||
* The OneView appliance
|
||||
|
||||
The role of ironic is to serve as a bare metal provider to OneView's managed
|
||||
physical hardware and to provide communication with other necessary OpenStack
|
||||
services such as Nova and Glance. When ironic receives a boot request, it
|
||||
works together with the ironic OneView driver to access a machine in OneView,
|
||||
the ``hpOneView`` being responsible for the communication with the OneView
|
||||
appliance.
|
||||
|
||||
From the Newton release on, OneView drivers enables a new feature called
|
||||
**dynamic allocation** of nodes [6]_. In this model, the driver allocates
|
||||
resources in OneView only at boot time, allowing idle resources in ironic
|
||||
to be used by OneView users, enabling actual resource sharing among ironic
|
||||
and OneView users.
|
||||
|
||||
Since OneView can claim nodes in ``available`` state at any time, a set of
|
||||
tasks runs periodically to detect nodes in use by OneView. A node in use by
|
||||
OneView is placed in ``manageable`` state and has maintenance mode set. Once
|
||||
the node is no longer in use, these tasks will make place them back in
|
||||
``available`` state and clear maintenance mode.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
* ``OneView appliance`` is the HP physical infrastructure manager to be
|
||||
integrated with the OneView driver.
|
||||
|
||||
Minimum version supported is 2.0.
|
||||
|
||||
* ``hpOneView`` is a python package containing a client to manage the
|
||||
communication between ironic and OneView.
|
||||
|
||||
Install the ``hpOneView`` module to enable the communication. Minimum version
|
||||
required is 4.4.0 but it is recommended to install the most up-to-date
|
||||
version::
|
||||
|
||||
$ pip install "hpOneView>=4.4.0"
|
||||
|
||||
* ``ironic-inspector`` if using hardware inspection.
|
||||
|
||||
Tested platforms
|
||||
================
|
||||
|
||||
* The OneView appliance used for testing was the OneView 2.0.
|
||||
|
||||
* The Enclosure used for testing was the ``BladeSystem c7000 Enclosure G2``.
|
||||
|
||||
* The driver should work on HP Proliant Gen8 and Gen9 Servers supported by
|
||||
OneView 2.0 and above, or any hardware whose network can be managed by
|
||||
OneView's ServerProfile. It has been tested with the following servers:
|
||||
|
||||
- Proliant BL460c Gen8
|
||||
- Proliant BL460c Gen9
|
||||
- Proliant BL465c Gen8
|
||||
- Proliant DL360 Gen9
|
||||
|
||||
Notice that for the driver to work correctly with Gen8 and Gen9 DL servers
|
||||
in general, the hardware also needs to run version 4.2.3 of iLO, with
|
||||
Redfish enabled.
|
||||
|
||||
Hardware Interfaces
|
||||
===================
|
||||
|
||||
The ``oneview`` hardware type supports the following hardware interfaces:
|
||||
|
||||
* boot
|
||||
Supports only ``pxe``. It can be enabled by using the
|
||||
``[DEFAULT]enabled_boot_interfaces`` option in ``ironic.conf``
|
||||
as given below:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_boot_interfaces = pxe
|
||||
|
||||
* console
|
||||
Supports only ``no-console``. It can be enabled by using the
|
||||
``[DEFAULT]enabled_console_interfaces``
|
||||
option in ``ironic.conf`` as given below:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_console_interfaces = no-console
|
||||
|
||||
* deploy
|
||||
Supports ``oneview-direct`` and ``oneview-iscsi``. The default is
|
||||
``oneview-iscsi``. They can be enabled by using the
|
||||
``[DEFAULT]enabled_deploy_interfaces`` option in ``ironic.conf``
|
||||
as given below:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_deploy_interfaces = oneview-iscsi,oneview-direct
|
||||
|
||||
* inspect
|
||||
Supports ``oneview`` and ``no-inspect``. The default is ``oneview``.
|
||||
They can be enabled by using the ``[DEFAULT]enabled_inspect_interfaces``
|
||||
option in ``ironic.conf`` as given below:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_inspect_interfaces = oneview,no-inspect
|
||||
|
||||
* management
|
||||
Supports only ``oneview``. It can be enabled by using the
|
||||
``[DEFAULT]enabled_management_interfaces`` option in ``ironic.conf`` as
|
||||
given below:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_management_interfaces = oneview
|
||||
|
||||
* power
|
||||
Supports only ``oneview``. It can be enabled by using the
|
||||
``[DEFAULT]enabled_power_interfaces`` option in ``ironic.conf`` as given
|
||||
below:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_power_interfaces = oneview
|
||||
|
||||
The ``oneview`` hardware type also supports the standard *network* and
|
||||
*storage* interfaces.
|
||||
|
||||
Here is an example of putting multiple interfaces configuration at once:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = oneview
|
||||
enabled_deploy_interfaces = oneview-direct,oneview-iscsi
|
||||
enabled_inspect_interfaces = oneview
|
||||
enabled_power_interfaces = oneview
|
||||
enabled_management_interfaces = oneview
|
||||
|
||||
Deploy process with oneview-iscsi deploy interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Admin configures the Proliant baremetal node to use ``oneview-iscsi``
|
||||
deploy interface.
|
||||
2. ironic gets a request to deploy a Glance image on the baremetal node.
|
||||
3. Driver sets the boot device to PXE.
|
||||
4. Driver powers on the baremetal node.
|
||||
5. ironic downloads the deploy and user images from a TFTP server.
|
||||
6. Driver reboots the baremetal node.
|
||||
7. User image is now deployed.
|
||||
8. Driver powers off the machine.
|
||||
9. Driver sets boot device to Disk.
|
||||
10. Driver powers on the machine.
|
||||
11. Baremetal node is active and ready to be used.
|
||||
|
||||
Deploy process with oneview-direct deploy interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Admin configures the Proliant baremetal node to use ``oneview-direct``
|
||||
deploy interface.
|
||||
2. ironic gets a request to deploy a Glance image on the baremetal node.
|
||||
3. Driver sets the boot device to PXE.
|
||||
4. Driver powers on the baremetal node.
|
||||
5. Node downloads the agent deploy images.
|
||||
6. Agent downloads the user images and writes it to disk.
|
||||
7. Driver reboots the baremetal node.
|
||||
8. User image is now deployed.
|
||||
9. Driver powers off the machine.
|
||||
10. Driver sets boot device to Disk.
|
||||
11. Driver powers on the machine.
|
||||
12. Baremetal node is active and ready to be used.
|
||||
|
||||
Hardware inspection
|
||||
===================
|
||||
|
||||
The OneView driver for ironic has the ability to do hardware inspection.
|
||||
Hardware inspection is the process of discovering hardware properties like
|
||||
memory size, CPU cores, processor architecture and disk size, of a given
|
||||
hardware. The OneView driver does in-band inspection, that involves booting a
|
||||
ramdisk on the hardware and fetching information directly from it. For that,
|
||||
your cloud controller needs to have the ``ironic-inspector`` component
|
||||
[9]_ running and properly enabled in ironic's configuration file.
|
||||
|
||||
See [10]_ for more information on how to install and configure
|
||||
``ironic-inspector``.
|
||||
|
||||
Registering a OneView node in ironic
|
||||
====================================
|
||||
|
||||
Nodes configured to use the OneView driver should have the ``driver``
|
||||
property set to ``oneview``. Considering our context, a node is the
|
||||
representation of a ``Server Hardware`` in OneView,
|
||||
and should be consistent with all its properties and related components, such
|
||||
as ``Server Hardware Type``, ``Server Profile Template``, ``Enclosure Group``,
|
||||
etc. In this case, to be enrolled, the node must have the following parameters:
|
||||
|
||||
* In ``driver_info``
|
||||
|
||||
- ``server_hardware_uri``: URI of the ``Server Hardware`` on OneView.
|
||||
|
||||
* In ``properties/capabilities``
|
||||
|
||||
- ``server_hardware_type_uri``: URI of the ``Server Hardware Type`` of the
|
||||
``Server Hardware``.
|
||||
- ``server_profile_template_uri``: URI of the ``Server Profile Template`` used
|
||||
to create the ``Server Profile`` of the ``Server Hardware``.
|
||||
- ``enclosure_group_uri`` (optional): URI of the ``Enclosure Group`` of the
|
||||
``Server Hardware``.
|
||||
|
||||
To enroll a node with the OneView driver using default values for the
|
||||
supported hardware interfaces, do::
|
||||
|
||||
$ openstack baremetal node create --driver oneview
|
||||
|
||||
To enroll a node with the OneView driver using specific hardware
|
||||
interfaces, do::
|
||||
|
||||
$ openstack baremetal node create --driver oneview \
|
||||
--deploy-interface oneview-direct \
|
||||
--power-interface oneview
|
||||
|
||||
To update the ``driver_info`` field of a newly enrolled OneView node, do::
|
||||
|
||||
$ openstack baremetal node set $NODE_UUID --driver-info server_hardware_uri=$SH_URI
|
||||
|
||||
To update the ``properties/capabilities`` namespace of a newly enrolled
|
||||
OneView node, do::
|
||||
|
||||
$ openstack baremetal node set $NODE_UUID \
|
||||
--property capabilities=server_hardware_type_uri:$SHT_URI,enclosure_group_uri:$EG_URI,server_profile_template_uri=$SPT_URI
|
||||
|
||||
In order to deploy, ironic will create and apply, at boot time, a ``Server
|
||||
Profile`` based on the ``Server Profile Template`` specified on the node to the
|
||||
``Server Hardware`` it represents on OneView. The URI of such ``Server Profile``
|
||||
will be stored in ``driver_info.applied_server_profile_uri`` field while the
|
||||
Server is allocated to ironic.
|
||||
|
||||
The ``Server Profile Templates`` and, therefore, the ``Server Profiles`` derived
|
||||
from them MUST comply with the following requirements:
|
||||
|
||||
* The option `MAC Address` in the `Advanced` section of
|
||||
``Server Profile``/``Server Profile Template`` should be set to `Physical`
|
||||
option;
|
||||
|
||||
* Their first `Connection` interface should be:
|
||||
|
||||
* Connected to ironic's provisioning network and;
|
||||
* The `Boot` option should be set to primary.
|
||||
|
||||
Node ports should be created considering the **MAC address of the first
|
||||
Interface** of the given ``Server Hardware``.
|
||||
|
||||
To tell ironic which NIC should be connected to the provisioning network, do::
|
||||
|
||||
$ openstack baremetal port create --node $NODE_UUID $MAC_ADDRESS
|
||||
|
||||
For more information on the enrollment process of an ironic node, see
|
||||
:ref:`enrollment`.
|
||||
|
||||
For more information on the definitions of ``Server Hardware``, ``Server
|
||||
Profile``, ``Server Profile Template`` and other OneView entities, refer to
|
||||
[1]_ or browse Help in your OneView appliance menu.
|
||||
|
||||
.. note::
|
||||
Ironic manages OneView machines either when they have
|
||||
a Server Profile applied by the driver or when they don't have any Server
|
||||
Profile. Trying to change the power state of the machine in OneView without
|
||||
first assigning a Server Profile will lead to allowing Ironic to revert the
|
||||
power state change. Ironic will NOT change the power state of machines
|
||||
which the Server Profile was applied by another OneView user.
|
||||
|
||||
3rd Party Tools
|
||||
===============
|
||||
|
||||
In order to ease user manual tasks, which are often time-consuming, we provide
|
||||
useful tools that work nicely with the OneView driver.
|
||||
|
||||
ironic-oneview-cli
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``ironic-oneView`` CLI is a command line interface for management tasks
|
||||
involving OneView nodes. Its features include a facility to create of ironic
|
||||
nodes with all required parameters for OneView nodes, creation of Nova flavors
|
||||
for OneView nodes.
|
||||
|
||||
For more details on how Ironic-OneView CLI works and how to set it up, see
|
||||
[8]_.
|
||||
|
||||
ironic-oneviewd
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The ``ironic-oneviewd`` daemon monitors the ironic inventory of resources and
|
||||
provides facilities to operators managing OneView driver deployments.
|
||||
|
||||
For more details on how Ironic-OneViewd works and how to set it up, see [7]_.
|
||||
|
||||
References
|
||||
==========
|
||||
.. [1] HP OneView - https://www.hpe.com/us/en/integrated-systems/software.html
|
||||
.. [6] Dynamic Allocation in OneView drivers - https://specs.openstack.org/openstack/ironic-specs/specs/not-implemented/oneview-drivers-dynamic-allocation.html
|
||||
.. [7] ironic-oneviewd - https://pypi.org/project/ironic-oneviewd/
|
||||
.. [8] ironic-oneview-cli - https://pypi.org/project/ironic-oneview-cli/
|
||||
.. [9] ironic-inspector - https://docs.openstack.org/ironic-inspector/latest/
|
||||
.. [10] ironic-inspector install - https://docs.openstack.org/ironic-inspector/latest/install/index.html
|
@ -11,10 +11,9 @@ to the target disk.
|
||||
iSCSI deploy
|
||||
============
|
||||
|
||||
With ``iscsi`` deploy interface (and also ``oneview-iscsi``, specific to the
|
||||
``oneview`` hardware type) the deploy ramdisk publishes the node's hard drive
|
||||
as an iSCSI_ share. The ironic-conductor then copies the image to this share.
|
||||
See :ref:`iSCSI deploy diagram <iscsi-deploy-example>` for a detailed
|
||||
With ``iscsi`` deploy interface, the deploy ramdisk publishes the node's hard
|
||||
drive as an iSCSI_ share. The ironic-conductor then copies the image to this
|
||||
share. See :ref:`iSCSI deploy diagram <iscsi-deploy-example>` for a detailed
|
||||
explanation of how this deploy interface works.
|
||||
|
||||
This interface is used by default, if enabled (see
|
||||
@ -31,8 +30,7 @@ when creating or updating a node::
|
||||
Direct deploy
|
||||
=============
|
||||
|
||||
With ``direct`` deploy interface (and also ``oneview-direct``, specific to the
|
||||
``oneview`` hardware type), the deploy ramdisk fetches the image from an
|
||||
With ``direct`` deploy interface, the deploy ramdisk fetches the image from an
|
||||
HTTP location. It can be an object storage (swift or RadosGW) temporary URL or
|
||||
a user-provided HTTP URL. The deploy ramdisk then copies the image to the
|
||||
target disk. See :ref:`direct deploy diagram <direct-deploy-example>` for
|
||||
@ -94,7 +92,7 @@ While somewhat more complex to set up, this deploy interface provides greater
|
||||
flexibility in terms of advanced node preparation during provisioning.
|
||||
|
||||
This interface is supported by most but not all hardware types declared
|
||||
in ironic (for example, ``oneview`` hardware type does not support it).
|
||||
in ironic.
|
||||
However this deploy interface is not enabled by default.
|
||||
To enable it, add ``ansible`` to the list of enabled deploy
|
||||
interfaces in ``enabled_deploy_interfaces`` option in the ``[DEFAULT]``
|
||||
|
@ -535,16 +535,6 @@ neutron:
|
||||
url = None
|
||||
url_timeout = 30
|
||||
|
||||
oneview:
|
||||
allow_insecure_connections = False
|
||||
enable_periodic_tasks = True
|
||||
manager_url = None
|
||||
max_polling_attempts = 12
|
||||
password = ***
|
||||
periodic_check_interval = 300
|
||||
tls_cacert_file = None
|
||||
username = None
|
||||
|
||||
oslo_concurrency:
|
||||
disable_process_locking = False
|
||||
lock_path = None
|
||||
|
@ -22,11 +22,9 @@ agent_ilo ilo ilo-virtual-media direct
|
||||
agent_ipmitool ipmi pxe direct inspector ipmitool ipmitool
|
||||
agent_ipmitool_socat ipmi pxe direct inspector ipmitool ipmitool
|
||||
agent_irmc irmc irmc-virtual-media direct irmc irmc irmc
|
||||
agent_pxe_oneview oneview pxe oneview-direct oneview oneview oneview
|
||||
agent_ucs cisco-ucs-managed pxe direct inspector ucsm ucsm
|
||||
iscsi_ilo ilo ilo-virtual-media iscsi ilo ilo ilo
|
||||
iscsi_irmc irmc irmc-virtual-media iscsi irmc irmc irmc
|
||||
iscsi_pxe_oneview oneview pxe oneview-iscsi oneview oneview oneview
|
||||
pxe_agent_cimc cisco-ucs-standalone pxe direct inspector cimc cimc
|
||||
pxe_drac idrac pxe iscsi idrac idrac idrac
|
||||
pxe_drac_inspector idrac pxe iscsi inspector idrac idrac
|
||||
|
@ -7,10 +7,7 @@
|
||||
proliantutils>=2.6.0
|
||||
pysnmp>=4.3.0,<5.0.0
|
||||
python-ironic-inspector-client>=1.5.0
|
||||
python-oneviewclient<3.0.0,>=2.5.2
|
||||
python-scciclient>=0.8.0
|
||||
python-ilorest-library>=2.1.0
|
||||
hpOneView>=4.4.0
|
||||
UcsSdk==0.8.2.2
|
||||
python-dracclient>=1.3.0
|
||||
python-xclarityclient>=0.1.6
|
||||
|
@ -693,15 +693,6 @@ class CIMCException(DriverOperationError):
|
||||
_msg_fmt = _("Cisco IMC exception occurred for node %(node)s: %(error)s")
|
||||
|
||||
|
||||
class OneViewError(DriverOperationError):
|
||||
_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'")
|
||||
|
||||
|
@ -38,7 +38,6 @@ from ironic.conf import iscsi
|
||||
from ironic.conf import metrics
|
||||
from ironic.conf import metrics_statsd
|
||||
from ironic.conf import neutron
|
||||
from ironic.conf import oneview
|
||||
from ironic.conf import pxe
|
||||
from ironic.conf import redfish
|
||||
from ironic.conf import service_catalog
|
||||
@ -71,7 +70,6 @@ iscsi.register_opts(CONF)
|
||||
metrics.register_opts(CONF)
|
||||
metrics_statsd.register_opts(CONF)
|
||||
neutron.register_opts(CONF)
|
||||
oneview.register_opts(CONF)
|
||||
pxe.register_opts(CONF)
|
||||
redfish.register_opts(CONF)
|
||||
service_catalog.register_opts(CONF)
|
||||
|
@ -1,50 +0,0 @@
|
||||
# Copyright 2016 Intel Corporation
|
||||
# Copyright 2015 Hewlett Packard Development Company, LP
|
||||
# Copyright 2015 Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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_config import cfg
|
||||
|
||||
from ironic.common.i18n import _
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('manager_url',
|
||||
help=_('URL where OneView is available.')),
|
||||
cfg.StrOpt('username',
|
||||
help=_('OneView username to be used.')),
|
||||
cfg.StrOpt('password',
|
||||
secret=True,
|
||||
help=_('OneView password to be used.')),
|
||||
cfg.BoolOpt('allow_insecure_connections',
|
||||
default=False,
|
||||
help=_('Option to allow insecure connection with OneView.')),
|
||||
cfg.StrOpt('tls_cacert_file',
|
||||
help=_('Path to CA certificate.')),
|
||||
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.')),
|
||||
]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_opts(opts, group='oneview')
|
@ -56,7 +56,6 @@ _opts = [
|
||||
('metrics', ironic.conf.metrics.opts),
|
||||
('metrics_statsd', ironic.conf.metrics_statsd.opts),
|
||||
('neutron', ironic.conf.neutron.list_opts()),
|
||||
('oneview', ironic.conf.oneview.opts),
|
||||
('pxe', ironic.conf.pxe.opts),
|
||||
('service_catalog', ironic.conf.service_catalog.list_opts()),
|
||||
('snmp', ironic.conf.snmp.opts),
|
||||
|
@ -1,553 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 re
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import importutils
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
hponeview_client = importutils.try_import('hpOneView.oneview_client')
|
||||
redfish = importutils.try_import('redfish')
|
||||
client_exception = importutils.try_import('hpOneView.exceptions')
|
||||
|
||||
REQUIRED_ON_DRIVER_INFO = {
|
||||
'server_hardware_uri': _("Server Hardware URI. Required in driver_info."),
|
||||
}
|
||||
|
||||
REQUIRED_ON_PROPERTIES = {
|
||||
'server_hardware_type_uri': _(
|
||||
"Server Hardware Type URI. Required in properties/capabilities."
|
||||
),
|
||||
'server_profile_template_uri': _(
|
||||
"Server Profile Template URI to clone from. "
|
||||
"Required in properties/capabilities."
|
||||
),
|
||||
}
|
||||
|
||||
OPTIONAL_ON_PROPERTIES = {
|
||||
'enclosure_group_uri': _(
|
||||
"Enclosure Group URI. Optional in properties/capabilities."),
|
||||
}
|
||||
|
||||
ILOREST_BASE_PORT = "443"
|
||||
|
||||
COMMON_PROPERTIES = {}
|
||||
COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO)
|
||||
COMMON_PROPERTIES.update(REQUIRED_ON_PROPERTIES)
|
||||
COMMON_PROPERTIES.update(OPTIONAL_ON_PROPERTIES)
|
||||
|
||||
# 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 prepare_manager_url(manager_url):
|
||||
# NOTE(mrtenio) python-oneviewclient uses https or http in the manager_url
|
||||
# while python-hpOneView does not. This will not be necessary when
|
||||
# python-hpOneView client is the only OneView library.
|
||||
if manager_url:
|
||||
url_match = "^(http[s]?://)?([^/]+)(/.*)?$"
|
||||
manager_url = re.search(url_match, manager_url).group(2)
|
||||
return manager_url
|
||||
|
||||
|
||||
def get_hponeview_client():
|
||||
"""Generate an instance of the hpOneView client.
|
||||
|
||||
Generates an instance of the hpOneView client using the hpOneView library.
|
||||
|
||||
:returns: an instance of the OneViewClient
|
||||
:raises: InvalidParameterValue if mandatory information is missing on the
|
||||
node or on invalid input.
|
||||
:raises: OneViewError if try a secure connection without CA certificate.
|
||||
"""
|
||||
manager_url = prepare_manager_url(CONF.oneview.manager_url)
|
||||
|
||||
insecure = CONF.oneview.allow_insecure_connections
|
||||
ssl_certificate = CONF.oneview.tls_cacert_file
|
||||
|
||||
if not (insecure or ssl_certificate):
|
||||
msg = _("TLS CA certificate to connect with OneView is missing.")
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
# NOTE(nicodemos) Ignore the CA certificate if it's an insecure connection
|
||||
if insecure and ssl_certificate:
|
||||
LOG.warning("Performing an insecure connection with OneView, the CA "
|
||||
"certificate file: %s will be ignored.", ssl_certificate)
|
||||
ssl_certificate = None
|
||||
|
||||
config = {
|
||||
"ip": manager_url,
|
||||
"credentials": {
|
||||
"userName": CONF.oneview.username,
|
||||
"password": CONF.oneview.password
|
||||
},
|
||||
"ssl_certificate": ssl_certificate
|
||||
}
|
||||
return hponeview_client.OneViewClient(config)
|
||||
|
||||
|
||||
def get_ilorest_client(server_hardware):
|
||||
"""Generate an instance of the iLORest library client.
|
||||
|
||||
:param: server_hardware: a server hardware uuid or uri
|
||||
:returns: an instance of the iLORest client
|
||||
:raises: InvalidParameterValue if mandatory information is missing on the
|
||||
node or on invalid input.
|
||||
"""
|
||||
oneview_client = get_hponeview_client()
|
||||
remote_console = oneview_client.server_hardware.get_remote_console_url(
|
||||
server_hardware
|
||||
)
|
||||
host_ip, ilo_token = _get_ilo_access(remote_console)
|
||||
base_url = "https://%s:%s" % (host_ip, ILOREST_BASE_PORT)
|
||||
return redfish.rest_client(base_url=base_url, sessionkey=ilo_token)
|
||||
|
||||
|
||||
def _get_ilo_access(remote_console):
|
||||
"""Get the needed information to access ilo.
|
||||
|
||||
Get the host_ip and a token of an iLO remote console instance which can be
|
||||
used to perform operations on that controller.
|
||||
|
||||
The Remote Console url has the following format:
|
||||
hplocons://addr=1.2.3.4&sessionkey=a79659e3b3b7c8209c901ac3509a6719
|
||||
|
||||
:param remote_console: OneView Remote Console object with a
|
||||
remoteConsoleUrl
|
||||
:returns: A tuple with the Host IP and Token to access ilo, for
|
||||
example: ('1.2.3.4', 'a79659e3b3b7c8209c901ac3509a6719')
|
||||
"""
|
||||
url = remote_console.get('remoteConsoleUrl')
|
||||
url_parse = parse.urlparse(url)
|
||||
host_ip = parse.parse_qs(url_parse.netloc).get('addr')[0]
|
||||
token = parse.parse_qs(url_parse.netloc).get('sessionkey')[0]
|
||||
return host_ip, token
|
||||
|
||||
|
||||
def verify_node_info(node):
|
||||
"""Verifies if fields and namespaces of a node are valid.
|
||||
|
||||
Verifies if the 'driver_info' field and the 'properties/capabilities'
|
||||
namespace exist and are not empty.
|
||||
|
||||
:param: node: node object to be verified
|
||||
:raises: InvalidParameterValue if required node capabilities and/or
|
||||
driver_info are malformed or missing
|
||||
:raises: MissingParameterValue if required node capabilities and/or
|
||||
driver_info are missing
|
||||
"""
|
||||
capabilities_dict = utils.capabilities_to_dict(
|
||||
node.properties.get('capabilities', '')
|
||||
)
|
||||
driver_info = node.driver_info
|
||||
|
||||
_verify_node_info('properties/capabilities', capabilities_dict,
|
||||
REQUIRED_ON_PROPERTIES)
|
||||
|
||||
_verify_node_info('driver_info', driver_info,
|
||||
REQUIRED_ON_DRIVER_INFO)
|
||||
|
||||
|
||||
def get_oneview_info(node):
|
||||
"""Gets OneView information from the node.
|
||||
|
||||
:param: node: node object to get information from
|
||||
:returns: a dictionary containing:
|
||||
:param server_hardware_uri: the uri of the server hardware in OneView
|
||||
:param server_hardware_type_uri: the uri of the server hardware type in
|
||||
OneView
|
||||
:param 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: OneViewInvalidNodeParameter if node capabilities are malformed
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
oneview_info = {
|
||||
'server_hardware_uri':
|
||||
driver_info.get('server_hardware_uri'),
|
||||
'server_hardware_type_uri':
|
||||
capabilities_dict.get('server_hardware_type_uri'),
|
||||
'enclosure_group_uri':
|
||||
capabilities_dict.get('enclosure_group_uri'),
|
||||
'server_profile_template_uri':
|
||||
capabilities_dict.get('server_profile_template_uri'),
|
||||
'applied_server_profile_uri':
|
||||
driver_info.get('applied_server_profile_uri'),
|
||||
}
|
||||
|
||||
return oneview_info
|
||||
|
||||
|
||||
def validate_oneview_resources_compatibility(task):
|
||||
"""Validate if the node configuration is consistent with OneView.
|
||||
|
||||
This method calls hpOneView functions to validate if the node
|
||||
configuration is consistent with the OneView resources it represents,
|
||||
including serverHardwareUri, serverHardwareTypeUri, serverGroupUri
|
||||
serverProfileTemplateUri, enclosureGroupUri and node ports. If any
|
||||
validation fails, the driver will raise an appropriate OneViewError.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: OneViewError if any validation fails.
|
||||
"""
|
||||
ports = task.ports
|
||||
oneview_client = get_hponeview_client()
|
||||
oneview_info = get_oneview_info(task.node)
|
||||
|
||||
_validate_node_server_profile_template(oneview_client, oneview_info)
|
||||
_validate_node_server_hardware_type(oneview_client, oneview_info)
|
||||
_validate_node_enclosure_group(oneview_client, oneview_info)
|
||||
_validate_server_profile_template_mac_type(oneview_client, oneview_info)
|
||||
_validate_node_port_mac_server_hardware(
|
||||
oneview_client, oneview_info, ports)
|
||||
|
||||
|
||||
def _verify_node_info(node_namespace, node_info_dict, info_required):
|
||||
"""Verify if info_required is present in node_namespace of the node info.
|
||||
|
||||
"""
|
||||
missing_keys = set(info_required) - set(node_info_dict)
|
||||
|
||||
if missing_keys:
|
||||
raise exception.MissingParameterValue(
|
||||
_("Missing the keys for the following OneView data in node's "
|
||||
"%(namespace)s: %(missing_keys)s.") %
|
||||
{'namespace': node_namespace,
|
||||
'missing_keys': ', '.join(missing_keys)
|
||||
}
|
||||
)
|
||||
|
||||
# False and 0 can still be considered as valid values
|
||||
missing_values_keys = [k for k in info_required
|
||||
if node_info_dict[k] in ('', None)]
|
||||
if missing_values_keys:
|
||||
missing_keys = ["%s:%s" % (node_namespace, k)
|
||||
for k in missing_values_keys]
|
||||
raise exception.MissingParameterValue(
|
||||
_("Missing parameter value for: '%s'") % "', '".join(missing_keys)
|
||||
)
|
||||
|
||||
|
||||
def node_has_server_profile(func):
|
||||
"""Checks if the node's Server Hardware has a Server Profile associated.
|
||||
|
||||
Decorator to execute before the function execution if the Server Profile
|
||||
is applied to the Server Hardware.
|
||||
|
||||
:param func: a given decorated function.
|
||||
"""
|
||||
def inner(self, *args, **kwargs):
|
||||
task = args[0]
|
||||
ensure_server_profile(task)
|
||||
return func(self, *args, **kwargs)
|
||||
return inner
|
||||
|
||||
|
||||
def ensure_server_profile(task):
|
||||
"""Checks if the node's Server Hardware has a Server Profile associated.
|
||||
|
||||
Function to check if the Server Profile is applied to the Server Hardware.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: OneViewError if failed to get server profile from OneView
|
||||
"""
|
||||
oneview_client = get_hponeview_client()
|
||||
try:
|
||||
profile_uri = task.node.driver_info.get('applied_server_profile_uri')
|
||||
oneview_client.server_profiles.get(profile_uri)
|
||||
except client_exception.HPOneViewException as exc:
|
||||
LOG.error(
|
||||
"Failed to get server profile: %(profile)s from OneView appliance "
|
||||
"for node %(node)s. Error: %(message)s", {
|
||||
"profile": profile_uri,
|
||||
"node": task.node.uuid,
|
||||
"message": exc
|
||||
}
|
||||
)
|
||||
raise exception.OneViewError(error=exc)
|
||||
|
||||
|
||||
def _get_server_hardware_mac_from_ilo(server_hardware):
|
||||
"""Get the MAC of Server Hardware's iLO controller.
|
||||
|
||||
:param: server_hardware: a server hardware uuid or uri
|
||||
:returns: MAC of Server Hardware's iLO controller.
|
||||
:raises: InvalidParameterValue if required iLO credentials are missing.
|
||||
:raises: OneViewError if can't get mac from a server hardware via iLO or
|
||||
if fails to get JSON object with the default path.
|
||||
"""
|
||||
try:
|
||||
ilo_client = get_ilorest_client(server_hardware)
|
||||
ilo_path = "/rest/v1/systems/1"
|
||||
hardware = jsonutils.loads(ilo_client.get(ilo_path).text)
|
||||
hardware_mac = hardware['HostCorrelation']['HostMACAddress'][0]
|
||||
except redfish.JsonDecodingError as exc:
|
||||
LOG.error("Failed in JSON object getting path: %s", ilo_path)
|
||||
raise exception.OneViewError(error=exc)
|
||||
except (ValueError, TypeError, IndexError) as exc:
|
||||
LOG.exception(
|
||||
"Failed to get mac from server hardware %(server_hardware)s "
|
||||
"via iLO. Error: %(message)s", {
|
||||
"server_hardware": server_hardware.get("uri"),
|
||||
"message": exc
|
||||
}
|
||||
)
|
||||
raise exception.OneViewError(error=exc)
|
||||
|
||||
return hardware_mac
|
||||
|
||||
|
||||
def _get_server_hardware_mac(server_hardware):
|
||||
"""Get the MAC address of the first PXE bootable port of an Ethernet port.
|
||||
|
||||
:param server_hardware: OneView Server Hardware object.
|
||||
:returns: MAC of the first Ethernet and function 'a' port of the
|
||||
Server Hardware object.
|
||||
:raises: OneViewError if there is no Ethernet port on the Server Hardware
|
||||
or if there is no portMap on the Server Hardware requested.
|
||||
"""
|
||||
sh_physical_port = None
|
||||
|
||||
if server_hardware.get('portMap'):
|
||||
for device in server_hardware.get(
|
||||
'portMap', {}).get('deviceSlots', ()):
|
||||
for physical_port in device.get('physicalPorts', ()):
|
||||
if physical_port.get('type') == 'Ethernet':
|
||||
sh_physical_port = physical_port
|
||||
break
|
||||
if sh_physical_port:
|
||||
for virtual_port in sh_physical_port.get('virtualPorts', ()):
|
||||
# NOTE(nicodemos): Ironic oneview drivers needs to use a
|
||||
# port that type is Ethernet and function identifier 'a' for
|
||||
# this FlexNIC to be able to make a deploy using PXE.
|
||||
if virtual_port.get('portFunction') == 'a':
|
||||
return virtual_port.get('mac', ()).lower()
|
||||
raise exception.OneViewError(
|
||||
_("There is no Ethernet port on the Server Hardware: %s") %
|
||||
server_hardware.get('uri'))
|
||||
else:
|
||||
raise exception.OneViewError(
|
||||
_("The Server Hardware: %s doesn't have a list of adapters/slots, "
|
||||
"their ports and attributes. This information is available only "
|
||||
"for blade servers. Is this a rack server?") %
|
||||
server_hardware.get('uri'))
|
||||
|
||||
|
||||
def _validate_node_server_profile_template(oneview_client, oneview_info):
|
||||
"""Validate if the Server Profile Template is consistent.
|
||||
|
||||
:param oneview_client: an instance of the HPE OneView client.
|
||||
:param oneview_info: the OneView related info in an Ironic node.
|
||||
:raises: OneViewError if the node's Server Profile Template is not
|
||||
consistent.
|
||||
"""
|
||||
server_profile_template = oneview_client.server_profile_templates.get(
|
||||
oneview_info['server_profile_template_uri'])
|
||||
server_hardware = oneview_client.server_hardware.get(
|
||||
oneview_info['server_hardware_uri'])
|
||||
|
||||
_validate_server_profile_template_server_hardware_type(
|
||||
server_profile_template, server_hardware)
|
||||
_validate_spt_enclosure_group(server_profile_template, server_hardware)
|
||||
_validate_server_profile_template_manage_boot(server_profile_template)
|
||||
|
||||
|
||||
def _validate_server_profile_template_server_hardware_type(
|
||||
server_profile_template, server_hardware):
|
||||
"""Validate if the Server Hardware Types are the same.
|
||||
|
||||
Validate if the Server Profile Template and the Server Hardware have the
|
||||
same Server Hardware Type.
|
||||
|
||||
:param server_profile_template: OneView Server Profile Template object.
|
||||
:param server_hardware: OneView Server Hardware object.
|
||||
:raises: OneViewError if the Server Profile Template and the Server
|
||||
Hardware does not have the same Server Hardware Type.
|
||||
"""
|
||||
spt_server_hardware_type_uri = (
|
||||
server_profile_template.get('serverHardwareTypeUri')
|
||||
)
|
||||
sh_server_hardware_type_uri = server_hardware.get('serverHardwareTypeUri')
|
||||
|
||||
if spt_server_hardware_type_uri != sh_server_hardware_type_uri:
|
||||
message = _(
|
||||
"Server profile template %(spt_uri)s serverHardwareTypeUri is "
|
||||
"inconsistent with server hardware %(server_hardware_uri)s "
|
||||
"serverHardwareTypeUri.") % {
|
||||
'spt_uri': server_profile_template.get('uri'),
|
||||
'server_hardware_uri': server_hardware.get('uri')}
|
||||
raise exception.OneViewError(message)
|
||||
|
||||
|
||||
def _validate_spt_enclosure_group(server_profile_template, server_hardware):
|
||||
"""Validate Server Profile Template's Enclosure Group and Hardware's.
|
||||
|
||||
:param server_profile_template: OneView Server Profile Template object.
|
||||
:param server_hardware: OneView Server Hardware object.
|
||||
:raises: OneViewError if the Server Profile Template's Enclosure Group does
|
||||
not match the Server Hardware's.
|
||||
"""
|
||||
spt_enclosure_group_uri = server_profile_template.get('enclosureGroupUri')
|
||||
sh_enclosure_group_uri = server_hardware.get('serverGroupUri')
|
||||
|
||||
if spt_enclosure_group_uri != sh_enclosure_group_uri:
|
||||
message = _("Server profile template %(spt_uri)s enclosureGroupUri is "
|
||||
"inconsistent with server hardware %(sh_uri)s "
|
||||
"serverGroupUri.") % {
|
||||
'spt_uri': server_profile_template.get('uri'),
|
||||
'sh_uri': server_hardware.get('uri')}
|
||||
raise exception.OneViewError(message)
|
||||
|
||||
|
||||
def _validate_server_profile_template_manage_boot(server_profile_template):
|
||||
"""Validate if the Server Profile Template allows to manage the boot order.
|
||||
|
||||
:param server_profile_template: OneView Server Profile Template object.
|
||||
:raises: OneViewError if the Server Profile Template does not allows to
|
||||
manage the boot order.
|
||||
"""
|
||||
manage_boot = server_profile_template.get('boot', {}).get('manageBoot')
|
||||
|
||||
if not manage_boot:
|
||||
message = _("Server Profile Template: %s, does not allow to manage "
|
||||
"boot order.") % server_profile_template.get('uri')
|
||||
raise exception.OneViewError(message)
|
||||
|
||||
|
||||
def _validate_node_server_hardware_type(oneview_client, oneview_info):
|
||||
"""Validate if the node's Server Hardware Type matches Server Hardware's.
|
||||
|
||||
:param: oneview_client: the HPE OneView Client.
|
||||
:param: oneview_info: the OneView related info in an Ironic node.
|
||||
:raises: OneViewError if the node's Server Hardware Type group doesn't
|
||||
match the Server Hardware's.
|
||||
"""
|
||||
node_server_hardware_type_uri = oneview_info['server_hardware_type_uri']
|
||||
server_hardware = oneview_client.server_hardware.get(
|
||||
oneview_info['server_hardware_uri'])
|
||||
server_hardware_sht_uri = server_hardware.get('serverHardwareTypeUri')
|
||||
|
||||
if server_hardware_sht_uri != node_server_hardware_type_uri:
|
||||
message = _("Node server_hardware_type_uri is inconsistent "
|
||||
"with OneView's server hardware %(server_hardware_uri)s "
|
||||
"serverHardwareTypeUri.") % {
|
||||
'server_hardware_uri': server_hardware.get('uri')}
|
||||
raise exception.OneViewError(message)
|
||||
|
||||
|
||||
def _validate_node_enclosure_group(oneview_client, oneview_info):
|
||||
"""Validate if the node's Enclosure Group matches the Server Hardware's.
|
||||
|
||||
:param oneview_client: an instance of the HPE OneView client.
|
||||
:param oneview_info: the OneView related info in an Ironic node.
|
||||
:raises: OneViewError if the node's enclosure group doesn't match the
|
||||
Server Hardware's.
|
||||
"""
|
||||
server_hardware = oneview_client.server_hardware.get(
|
||||
oneview_info['server_hardware_uri'])
|
||||
sh_enclosure_group_uri = server_hardware.get('serverGroupUri')
|
||||
node_enclosure_group_uri = oneview_info['enclosure_group_uri']
|
||||
|
||||
if node_enclosure_group_uri and (
|
||||
sh_enclosure_group_uri != node_enclosure_group_uri):
|
||||
message = _(
|
||||
"Node enclosure_group_uri '%(node_enclosure_group_uri)s' "
|
||||
"is inconsistent with OneView's server hardware "
|
||||
"serverGroupUri '%(sh_enclosure_group_uri)s' of "
|
||||
"ServerHardware %(server_hardware)s") % {
|
||||
'node_enclosure_group_uri': node_enclosure_group_uri,
|
||||
'sh_enclosure_group_uri': sh_enclosure_group_uri,
|
||||
'server_hardware': server_hardware.get('uri')}
|
||||
raise exception.OneViewError(message)
|
||||
|
||||
|
||||
def _validate_node_port_mac_server_hardware(oneview_client,
|
||||
oneview_info, ports):
|
||||
"""Validate if a port matches the node's Server Hardware's MAC.
|
||||
|
||||
:param oneview_client: an instance of the HPE OneView client.
|
||||
:param oneview_info: the OneView related info in an Ironic node.
|
||||
:param ports: a list of Ironic node's ports.
|
||||
:raises: OneViewError if there is no port with MAC address matching one
|
||||
in OneView.
|
||||
|
||||
"""
|
||||
server_hardware = oneview_client.server_hardware.get(
|
||||
oneview_info['server_hardware_uri'])
|
||||
|
||||
if not ports:
|
||||
return
|
||||
|
||||
# NOTE(nicodemos) If hponeview client's unable to get the MAC of the Server
|
||||
# Hardware and raises an exception, the driver will try to get it from
|
||||
# the iLOrest client.
|
||||
try:
|
||||
mac = _get_server_hardware_mac(server_hardware)
|
||||
except exception.OneViewError:
|
||||
mac = _get_server_hardware_mac_from_ilo(server_hardware)
|
||||
|
||||
incompatible_macs = []
|
||||
for port in ports:
|
||||
if port.address.lower() == mac.lower():
|
||||
return
|
||||
incompatible_macs.append(port.address)
|
||||
|
||||
message = _("The ports of the node are not compatible with its "
|
||||
"server hardware %(server_hardware_uri)s. There are no Ironic "
|
||||
"port MAC's: %(port_macs)s, that matches with the "
|
||||
"server hardware's MAC: %(server_hardware_mac)s") % {
|
||||
'server_hardware_uri': server_hardware.get('uri'),
|
||||
'port_macs': ', '.join(incompatible_macs),
|
||||
'server_hardware_mac': mac}
|
||||
raise exception.OneViewError(message)
|
||||
|
||||
|
||||
def _validate_server_profile_template_mac_type(oneview_client, oneview_info):
|
||||
"""Validate if the node's Server Profile Template's MAC type is physical.
|
||||
|
||||
:param oneview_client: an instance of the HPE OneView client.
|
||||
:param oneview_info: the OneView related info in an Ironic node.
|
||||
:raises: OneViewError if the node's Server Profile Template's MAC type is
|
||||
not physical.
|
||||
"""
|
||||
server_profile_template = oneview_client.server_profile_templates.get(
|
||||
oneview_info['server_profile_template_uri']
|
||||
)
|
||||
if server_profile_template.get('macType') != 'Physical':
|
||||
message = _("The server profile template %s is not set to use "
|
||||
"physical MAC.") % server_profile_template.get('uri')
|
||||
raise exception.OneViewError(message)
|
@ -1,308 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 ironic_lib import metrics_utils
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import utils
|
||||
from ironic.conf import CONF
|
||||
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__)
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class OneViewPeriodicTasks(object):
|
||||
|
||||
@periodics.periodic(spacing=CONF.oneview.periodic_check_interval,
|
||||
enabled=CONF.oneview.enable_periodic_tasks
|
||||
and CONF.oneview.periodic_check_interval > 0)
|
||||
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': 'oneview'
|
||||
}
|
||||
node_iter = manager.iter_nodes(filters=filters)
|
||||
|
||||
for node_uuid, driver, conductor_group 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:
|
||||
# NOTE(xavierr): Skip this node and process the
|
||||
# remaining nodes. This node will be checked in
|
||||
# the next periodic call.
|
||||
|
||||
LOG.error("Error while determining if node "
|
||||
"%(node_uuid)s is in use by OneView. "
|
||||
"Error: %(error)s",
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
|
||||
continue
|
||||
|
||||
if oneview_using:
|
||||
purpose_msg = ('Updating node %(node_uuid)s in use '
|
||||
'by OneView from %(provision_state)s state '
|
||||
'to %(target_state)s state and maintenance '
|
||||
'mode %(maintenance)s.')
|
||||
purpose_data = {'node_uuid': node_uuid,
|
||||
'provision_state': states.AVAILABLE,
|
||||
'target_state': states.MANAGEABLE,
|
||||
'maintenance': True}
|
||||
|
||||
LOG.info(purpose_msg, purpose_data)
|
||||
|
||||
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
|
||||
and CONF.oneview.periodic_check_interval > 0)
|
||||
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': 'oneview'
|
||||
}
|
||||
node_iter = manager.iter_nodes(fields=['maintenance_reason'],
|
||||
filters=filters)
|
||||
for (node_uuid, driver, conductor_group,
|
||||
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:
|
||||
# NOTE(xavierr): Skip this node and process the
|
||||
# remaining nodes. This node will be checked in
|
||||
# the next periodic call.
|
||||
|
||||
LOG.error("Error while determining if node "
|
||||
"%(node_uuid)s is in use by OneView. "
|
||||
"Error: %(error)s",
|
||||
{'node_uuid': node.uuid, 'error': e})
|
||||
|
||||
continue
|
||||
|
||||
if not oneview_using:
|
||||
purpose_msg = ('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.')
|
||||
purpose_data = {'node_uuid': node_uuid,
|
||||
'provision_state': states.MANAGEABLE,
|
||||
'target_state': states.AVAILABLE,
|
||||
'maintenance': False}
|
||||
|
||||
LOG.info(purpose_msg, purpose_data)
|
||||
|
||||
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': 'oneview'
|
||||
}
|
||||
node_iter = manager.iter_nodes(fields=['driver_internal_info'],
|
||||
filters=filters)
|
||||
|
||||
for (node_uuid, driver, conductor_group,
|
||||
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_msg = ('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.')
|
||||
purpose_dict = {'node_uuid': node_uuid,
|
||||
'provision_state': states.CLEANFAIL,
|
||||
'target_state': states.MANAGEABLE,
|
||||
'maintenance': False}
|
||||
|
||||
LOG.info(purpose_msg, purpose_dict)
|
||||
|
||||
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."""
|
||||
|
||||
# NOTE(TheJulia): Marking as unsupported as 3rd party CI was taken down
|
||||
# shortly before the beginning of the Rocky cycle, and no replies have
|
||||
# indicated that 3rd party CI will be re-established nor visible
|
||||
# actions observed regarding re-establishing 3rd party CI.
|
||||
# TODO(TheJulia): This should be expected to be removed in Stein.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return deploy_utils.get_properties()
|
||||
|
||||
@METRICS.timer('OneViewIscsiDeploy.validate')
|
||||
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)
|
||||
|
||||
@METRICS.timer('OneViewIscsiDeploy.prepare')
|
||||
def prepare(self, task):
|
||||
deploy_utils.prepare(task)
|
||||
super(OneViewIscsiDeploy, self).prepare(task)
|
||||
|
||||
@METRICS.timer('OneViewIscsiDeploy.tear_down')
|
||||
def tear_down(self, task):
|
||||
# teardown if automated clean is disabled on the node
|
||||
# or if general automated clean is not enabled generally
|
||||
# and not on the node as well
|
||||
if utils.skip_automated_cleaning(task.node):
|
||||
deploy_utils.tear_down(task)
|
||||
return super(OneViewIscsiDeploy, self).tear_down(task)
|
||||
|
||||
@METRICS.timer('OneViewIscsiDeploy.prepare_cleaning')
|
||||
def prepare_cleaning(self, task):
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
return super(OneViewIscsiDeploy, self).prepare_cleaning(task)
|
||||
|
||||
@METRICS.timer('OneViewIscsiDeploy.tear_down_cleaning')
|
||||
def tear_down_cleaning(self, task):
|
||||
deploy_utils.tear_down_cleaning(task)
|
||||
super(OneViewIscsiDeploy, self).tear_down_cleaning(task)
|
||||
|
||||
|
||||
class OneViewAgentDeploy(agent.AgentDeploy, OneViewPeriodicTasks):
|
||||
"""Class for OneView Agent deployment driver."""
|
||||
|
||||
# NOTE(TheJulia): Marking as unsupported as 3rd party CI was taken down
|
||||
# shortly before the beginning of the Rocky cycle, and no replies have
|
||||
# indicated that 3rd party CI will be re-established nor visible
|
||||
# actions observed regarding re-establishing 3rd party CI.
|
||||
# TODO(TheJulia): This should be expected to be removed in Stein.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return deploy_utils.get_properties()
|
||||
|
||||
@METRICS.timer('OneViewAgentDeploy.validate')
|
||||
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)
|
||||
|
||||
@METRICS.timer('OneViewAgentDeploy.prepare')
|
||||
def prepare(self, task):
|
||||
deploy_utils.prepare(task)
|
||||
super(OneViewAgentDeploy, self).prepare(task)
|
||||
|
||||
@METRICS.timer('OneViewAgentDeploy.tear_down')
|
||||
def tear_down(self, task):
|
||||
# if node specifically has cleanup disabled, or general cleanup
|
||||
# is disabled and node has not it enabled
|
||||
if utils.skip_automated_cleaning(task.node):
|
||||
deploy_utils.tear_down(task)
|
||||
return super(OneViewAgentDeploy, self).tear_down(task)
|
||||
|
||||
@METRICS.timer('OneViewAgentDeploy.prepare_cleaning')
|
||||
def prepare_cleaning(self, task):
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
return super(OneViewAgentDeploy, self).prepare_cleaning(task)
|
||||
|
||||
@METRICS.timer('OneViewAgentDeploy.tear_down_cleaning')
|
||||
def tear_down_cleaning(self, task):
|
||||
deploy_utils.tear_down_cleaning(task)
|
||||
super(OneViewAgentDeploy, self).tear_down_cleaning(task)
|
@ -1,384 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 operator
|
||||
|
||||
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 import states
|
||||
from ironic.drivers.modules.oneview import common
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
client_exception = importutils.try_import('hpOneView.exceptions')
|
||||
|
||||
|
||||
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)
|
||||
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)
|
||||
except exception.OneViewError as e:
|
||||
raise exception.NodeCleaningFailure(node=task.node.uuid, reason=e)
|
||||
|
||||
|
||||
def _create_profile_from_template(
|
||||
oneview_client, server_profile_name,
|
||||
server_hardware_uri, server_profile_template):
|
||||
"""Create a server profile from a server profile template.
|
||||
|
||||
:param oneview_client: an HPE OneView Client instance
|
||||
:param server_profile_name: the name of the new server profile
|
||||
:param server_hardware_uri: the server_hardware assigned to server profile
|
||||
:param server_profile_template: the server profile template id or uri
|
||||
:returns: The new server profile generated with the name and server
|
||||
hardware passed on parameters
|
||||
:raises OneViewError: if the communication with OneView fails
|
||||
|
||||
"""
|
||||
server_profile = oneview_client.server_profile_templates.get_new_profile(
|
||||
server_profile_template
|
||||
)
|
||||
server_profile['name'] = server_profile_name
|
||||
server_profile['serverHardwareUri'] = server_hardware_uri
|
||||
server_profile['serverProfileTemplateUri'] = ""
|
||||
try:
|
||||
return oneview_client.server_profiles.create(server_profile)
|
||||
except client_exception.HPOneViewException as e:
|
||||
msg = (_("Error while creating a Server Profile for Server Hardware: "
|
||||
"%(sh_uri)s. Error: %(error)s") %
|
||||
{'sh_uri': server_hardware_uri, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
|
||||
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 server_hardware: Server Hardware object.
|
||||
:param applied_sp_uri: Server Profile URI applied in the node.
|
||||
: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.
|
||||
: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
|
||||
server_profile_uri = server_hardware.get('serverProfileUri')
|
||||
return (server_profile_uri
|
||||
and operation(applied_sp_uri, server_profile_uri))
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
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(oneview_client, 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.
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
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(oneview_client, node,
|
||||
predicate, positive, negative)
|
||||
|
||||
|
||||
def _check_applied_server_profile(oneview_client, node,
|
||||
predicate, positive, negative):
|
||||
"""Check if node is in use by ironic in OneView.
|
||||
|
||||
:param oneview_client: an instance of the OneView client
|
||||
: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.
|
||||
|
||||
"""
|
||||
oneview_info = common.get_oneview_info(node)
|
||||
try:
|
||||
server_hardware = oneview_client.server_hardware.get(
|
||||
oneview_info.get('server_hardware_uri')
|
||||
)
|
||||
except client_exception.HPOneViewResourceNotFound 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')
|
||||
result = predicate(server_hardware, applied_sp_uri)
|
||||
|
||||
if result:
|
||||
LOG.debug(positive)
|
||||
else:
|
||||
LOG.debug(negative)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
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
|
||||
:param applied_profile: the server_profile that will be applied to node
|
||||
"""
|
||||
driver_info = node.driver_info
|
||||
driver_info['applied_server_profile_uri'] = applied_profile.get('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 or the node is already in use by OneView.
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
node_in_use_by_oneview = is_node_in_use_by_oneview(node)
|
||||
|
||||
if not node_in_use_by_oneview:
|
||||
|
||||
oneview_info = common.get_oneview_info(node)
|
||||
|
||||
applied_sp_uri = node.driver_info.get('applied_server_profile_uri')
|
||||
sh_uri = oneview_info.get("server_hardware_uri")
|
||||
spt_uri = oneview_info.get("server_profile_template_uri")
|
||||
server_hardware = oneview_client.server_hardware.get(sh_uri)
|
||||
|
||||
if not server_hardware:
|
||||
msg = _("An error occurred during allocating server hardware to "
|
||||
"ironic. Server hardware: %s not found.") % sh_uri
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
# Don't have Server Profile on OneView but has
|
||||
# `applied_server_profile_uri` on driver_info
|
||||
if not server_hardware.get('serverProfileUri') and applied_sp_uri:
|
||||
|
||||
_del_applied_server_profile_uri_field(node)
|
||||
LOG.info(
|
||||
"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.get(
|
||||
'serverProfileUri') == applied_sp_uri):
|
||||
LOG.info(
|
||||
"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 = _create_profile_from_template(
|
||||
oneview_client, server_profile_name, sh_uri, spt_uri
|
||||
)
|
||||
_add_applied_server_profile_uri_field(node, applied_profile)
|
||||
|
||||
LOG.info(
|
||||
"Server Profile %(server_profile_uuid)s was successfully"
|
||||
" applied to node %(node_uuid)s.",
|
||||
{"node_uuid": node.uuid,
|
||||
"server_profile_uuid": applied_profile.get('uri')}
|
||||
)
|
||||
|
||||
except client_exception.HPOneViewInvalidResource as e:
|
||||
LOG.error("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(task):
|
||||
"""Deallocate Server Hardware from ironic.
|
||||
|
||||
:param task: a TaskManager object
|
||||
:raises OneViewError: if an error occurs while deallocating the Server
|
||||
Hardware to ironic
|
||||
|
||||
"""
|
||||
node = task.node
|
||||
oneview_client = common.get_hponeview_client()
|
||||
|
||||
if is_node_in_use_by_ironic(node):
|
||||
oneview_info = common.get_oneview_info(node)
|
||||
server_profile_uri = oneview_info.get('applied_server_profile_uri')
|
||||
|
||||
try:
|
||||
task.driver.power.set_power_state(task, states.POWER_OFF)
|
||||
oneview_client.server_profiles.delete(server_profile_uri)
|
||||
_del_applied_server_profile_uri_field(node)
|
||||
LOG.info("Server Profile %(server_profile_uri)s was deleted "
|
||||
"from node %(node_uuid)s in OneView.",
|
||||
{'server_profile_uri': server_profile_uri,
|
||||
'node_uuid': node.uuid})
|
||||
except client_exception.HPOneViewException 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(error=msg)
|
||||
|
||||
else:
|
||||
LOG.warning("Cannot deallocate node %(node_uuid)s "
|
||||
"in OneView because it is not in use by "
|
||||
"ironic.", {'node_uuid': node.uuid})
|
@ -1,100 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 ironic_lib import metrics_utils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers.modules import inspector
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
|
||||
class OneViewInspect(inspector.Inspector):
|
||||
"""Interface for in band inspection."""
|
||||
|
||||
# NOTE(TheJulia): Marking as unsupported as 3rd party CI was taken down
|
||||
# shortly before the beginning of the Rocky cycle, and no replies have
|
||||
# indicated that 3rd party CI will be re-established nor visible
|
||||
# actions observed regarding re-establishing 3rd party CI.
|
||||
# TODO(TheJulia): This should be expected to be removed in Stein.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return deploy_utils.get_properties()
|
||||
|
||||
@METRICS.timer('OneViewInspect.validate')
|
||||
def validate(self, task):
|
||||
"""Checks required info on 'driver_info' and validates node for 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)
|
||||
|
||||
@METRICS.timer('OneViewInspect.inspect_hardware')
|
||||
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)
|
||||
def _periodic_check_result(self, manager, context):
|
||||
filters = {'provision_state': states.INSPECTWAIT,
|
||||
'driver': 'oneview'}
|
||||
node_iter = manager.iter_nodes(filters=filters)
|
||||
|
||||
for node_uuid, driver, conductor_group in node_iter:
|
||||
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.INSPECTWAIT and state_after in [
|
||||
states.MANAGEABLE, states.INSPECTFAIL
|
||||
]:
|
||||
deploy_utils.deallocate_server_hardware_from_ironic(task.node)
|
||||
|
||||
return result
|
@ -1,325 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 ironic_lib import metrics_utils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
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
|
||||
|
||||
client_exception = importutils.try_import('hpOneView.exceptions')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
BOOT_DEVICE_MAP_ONEVIEW = {
|
||||
boot_devices.CDROM: 'CD',
|
||||
boot_devices.DISK: 'HardDisk',
|
||||
boot_devices.PXE: 'PXE'
|
||||
}
|
||||
|
||||
BOOT_DEVICE_MAP_ONEVIEW_REV = {
|
||||
v: k for k, v in BOOT_DEVICE_MAP_ONEVIEW.items()}
|
||||
|
||||
BOOT_DEVICE_MAP_ILO = {
|
||||
boot_devices.CDROM: 'Cd',
|
||||
boot_devices.DISK: 'Hdd',
|
||||
boot_devices.PXE: 'Pxe'
|
||||
}
|
||||
|
||||
BOOT_DEVICE_MAP_ILO_REV = {
|
||||
v: k for k, v in BOOT_DEVICE_MAP_ILO.items()}
|
||||
|
||||
ILO_SYSTEM_PATH = "/rest/v1/Systems/1"
|
||||
|
||||
ILO_REQUEST_HEADERS = {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
def set_onetime_boot(task):
|
||||
"""Set onetime boot to server hardware.
|
||||
|
||||
Change the onetime boot option of an OneView server hardware.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
"""
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = driver_internal_info.get('next_boot_device')
|
||||
|
||||
if not next_boot_device:
|
||||
return
|
||||
|
||||
boot_device = next_boot_device.get('boot_device')
|
||||
persistent = next_boot_device.get('persistent')
|
||||
|
||||
if persistent:
|
||||
return
|
||||
|
||||
server_hardware = task.node.driver_info.get('server_hardware_uri')
|
||||
ilo_client = common.get_ilorest_client(server_hardware)
|
||||
boot_device = BOOT_DEVICE_MAP_ILO.get(boot_device)
|
||||
body = {
|
||||
"Boot": {
|
||||
"BootSourceOverrideTarget": boot_device,
|
||||
"BootSourceOverrideEnabled": "Once"
|
||||
}
|
||||
}
|
||||
try:
|
||||
ilo_client.patch(path=ILO_SYSTEM_PATH, body=body,
|
||||
headers=ILO_REQUEST_HEADERS)
|
||||
except Exception as e:
|
||||
msg = (_("Error while trying to set onetime boot on Server Hardware: "
|
||||
"%(sh_uri)s. Error: %(error)s") %
|
||||
{'sh_uri': server_hardware, 'error': e})
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
|
||||
def _is_onetime_boot(task):
|
||||
"""Check onetime boot from server hardware.
|
||||
|
||||
Check if the onetime boot option of an OneView server hardware
|
||||
is set to 'Once' in iLO.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: Boolean value. True if onetime boot is 'Once'
|
||||
False otherwise.
|
||||
"""
|
||||
server_hardware = task.node.driver_info.get('server_hardware_uri')
|
||||
ilo_client = common.get_ilorest_client(server_hardware)
|
||||
response = ilo_client.get(path=ILO_SYSTEM_PATH,
|
||||
headers=ILO_REQUEST_HEADERS)
|
||||
onetime_boot = None
|
||||
boot = response.dict.get('Boot', {})
|
||||
if boot:
|
||||
onetime_boot = boot.get('BootSourceOverrideEnabled')
|
||||
return onetime_boot == 'Once'
|
||||
|
||||
|
||||
def set_boot_device(task):
|
||||
"""Sets the boot device for a node.
|
||||
|
||||
Sets the boot device to use on next reboot of the node.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: InvalidParameterValue if an invalid boot device is
|
||||
specified.
|
||||
:raises: OneViewError if the communication with OneView fails
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
common.ensure_server_profile(task)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = driver_internal_info.get('next_boot_device')
|
||||
|
||||
if next_boot_device:
|
||||
boot_device = next_boot_device.get('boot_device')
|
||||
persistent = next_boot_device.get('persistent')
|
||||
|
||||
if boot_device not in sorted(BOOT_DEVICE_MAP_ONEVIEW):
|
||||
raise exception.InvalidParameterValue(
|
||||
_("Invalid boot device %s specified.") % boot_device)
|
||||
|
||||
LOG.debug("Setting boot device to %(boot_device)s and "
|
||||
"persistent to %(persistent)s for node %(node)s",
|
||||
{"boot_device": boot_device, "persistent": persistent,
|
||||
"node": task.node.uuid})
|
||||
|
||||
profile = task.node.driver_info.get('applied_server_profile_uri')
|
||||
boot_device = BOOT_DEVICE_MAP_ONEVIEW.get(boot_device)
|
||||
|
||||
try:
|
||||
server_profile = oneview_client.server_profiles.get(profile)
|
||||
boot = server_profile.get('boot', {})
|
||||
order = boot.get('order', [])
|
||||
if boot_device in order:
|
||||
order.remove(boot_device)
|
||||
order.insert(0, boot_device)
|
||||
boot['order'] = order
|
||||
server_profile['boot'] = boot
|
||||
oneview_client.server_profiles.update(server_profile, profile)
|
||||
set_onetime_boot(task)
|
||||
driver_internal_info.pop('next_boot_device', None)
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
except client_exception.HPOneViewException as oneview_exc:
|
||||
msg = (_(
|
||||
"Error setting boot device on OneView. Error: %s")
|
||||
% oneview_exc
|
||||
)
|
||||
raise exception.OneViewError(error=msg)
|
||||
|
||||
else:
|
||||
LOG.debug("Not going to set boot device because there is no "
|
||||
"'next_boot_device' on driver_internal_info "
|
||||
"for the %(node)s",
|
||||
{"node": task.node.uuid})
|
||||
|
||||
|
||||
class OneViewManagement(base.ManagementInterface):
|
||||
|
||||
# NOTE(TheJulia): Marking as unsupported as 3rd party CI was taken down
|
||||
# shortly before the beginning of the Rocky cycle, and no replies have
|
||||
# indicated that 3rd party CI will be re-established nor visible
|
||||
# actions observed regarding re-establishing 3rd party CI.
|
||||
# TODO(TheJulia): This should be expected to be removed in Stein.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return deploy_utils.get_properties()
|
||||
|
||||
@METRICS.timer('OneViewManagement.validate')
|
||||
def validate(self, task):
|
||||
"""Checks required info on 'driver_info' and validates node for 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, and if
|
||||
the server hardware attributes (ram, memory, vcpus count) are
|
||||
consistent with OneView.
|
||||
|
||||
: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)
|
||||
|
||||
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)
|
||||
|
||||
@METRICS.timer('OneViewManagement.get_supported_boot_devices')
|
||||
def get_supported_boot_devices(self, task):
|
||||
"""Gets a list of the supported boot devices.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: A list with the supported boot devices defined
|
||||
in :mod:`ironic.common.boot_devices`.
|
||||
"""
|
||||
|
||||
return sorted(BOOT_DEVICE_MAP_ONEVIEW)
|
||||
|
||||
@METRICS.timer('OneViewManagement.set_boot_device')
|
||||
@task_manager.require_exclusive_lock
|
||||
@common.node_has_server_profile
|
||||
def set_boot_device(self, task, device, persistent=False):
|
||||
"""Set the next boot device to the node.
|
||||
|
||||
Sets the boot device to the node next_boot_device on
|
||||
driver_internal_info namespace. The operation will be
|
||||
performed before the next node power on.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:param device: the boot device, one of the supported devices
|
||||
listed in :mod:`ironic.common.boot_devices`.
|
||||
:param persistent: Boolean value. True if the boot device will
|
||||
persist to all future boots, False if not.
|
||||
Default: False.
|
||||
:raises: InvalidParameterValue if an invalid boot device is
|
||||
specified.
|
||||
"""
|
||||
if device not in self.get_supported_boot_devices(task):
|
||||
raise exception.InvalidParameterValue(
|
||||
_("Invalid boot device %s specified.") % device)
|
||||
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'boot_device': device,
|
||||
'persistent': persistent}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
LOG.debug("The 'next_boot_device' flag was updated on "
|
||||
"driver_internal_info with device=%(boot_device)s "
|
||||
"and persistent=%(persistent)s for the node "
|
||||
"%(node)s",
|
||||
{"boot_device": device, "persistent": persistent,
|
||||
"node": task.node.uuid})
|
||||
|
||||
@METRICS.timer('OneViewManagement.get_boot_device')
|
||||
@common.node_has_server_profile
|
||||
def get_boot_device(self, task):
|
||||
"""Get the current boot device from the node.
|
||||
|
||||
Gets the boot device from the node 'next_boot_device on
|
||||
driver_internal_info namespace if exists. Gets through
|
||||
a request to OneView otherwise.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: a dictionary containing:
|
||||
:boot_device: the boot device, one of
|
||||
:mod:`ironic.common.boot_devices` [PXE, DISK, CDROM]
|
||||
:persistent: Whether the boot device will persist to all
|
||||
future boots or not, None if it is unknown.
|
||||
:raises: InvalidParameterValue if the boot device is unknown
|
||||
:raises: OneViewError if the communication with OneView fails
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = driver_internal_info.get('next_boot_device')
|
||||
|
||||
if next_boot_device:
|
||||
return next_boot_device
|
||||
|
||||
driver_info = task.node.driver_info
|
||||
server_profile = driver_info.get('applied_server_profile_uri')
|
||||
|
||||
try:
|
||||
profile = oneview_client.server_profiles.get(server_profile)
|
||||
primary_device = None
|
||||
boot = profile.get('boot', {})
|
||||
boot_order = boot.get('order', [])
|
||||
if boot_order:
|
||||
primary_device = boot_order[0]
|
||||
except client_exception.HPOneViewException as exc:
|
||||
msg = _("Error on node: %(node)s while getting Server Profile: "
|
||||
"%(profile)s of the from OneView. Error: %(error)s.") % {
|
||||
'profile': server_profile,
|
||||
'node': task.node.uuid,
|
||||
'error': exc
|
||||
}
|
||||
raise exception.OneViewError(msg)
|
||||
|
||||
if primary_device not in BOOT_DEVICE_MAP_ONEVIEW_REV:
|
||||
raise exception.InvalidParameterValue(
|
||||
_("Unsupported boot device %(device)s for node: %(node)s")
|
||||
% {"device": primary_device, "node": task.node.uuid}
|
||||
)
|
||||
|
||||
boot_device = {
|
||||
'boot_device': BOOT_DEVICE_MAP_ONEVIEW_REV.get(primary_device),
|
||||
'persistent': not _is_onetime_boot(task)
|
||||
}
|
||||
|
||||
return boot_device
|
||||
|
||||
@METRICS.timer('OneViewManagement.get_sensors_data')
|
||||
def get_sensors_data(self, task):
|
||||
"""Get sensors data.
|
||||
|
||||
Not implemented by this driver.
|
||||
:param task: a TaskManager instance.
|
||||
"""
|
||||
raise NotImplementedError()
|
@ -1,224 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 ironic_lib import metrics_utils
|
||||
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 import states
|
||||
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
|
||||
from ironic.drivers.modules.oneview import management
|
||||
|
||||
client_exception = importutils.try_import('hpOneView.exceptions')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
POWER_ON = {'powerState': 'On'}
|
||||
POWER_OFF = {'powerState': 'Off', 'powerControl': 'PressAndHold'}
|
||||
REBOOT = {'powerState': 'On', 'powerControl': 'ColdBoot'}
|
||||
SOFT_REBOOT = {'powerState': 'On', 'powerControl': 'Reset'}
|
||||
SOFT_POWER_OFF = {'powerState': 'Off', 'powerControl': 'MomentaryPress'}
|
||||
|
||||
GET_POWER_STATE_MAP = {
|
||||
'On': states.POWER_ON,
|
||||
'Off': states.POWER_OFF,
|
||||
'Resetting': states.REBOOT,
|
||||
'PoweringOff': states.POWER_ON,
|
||||
'PoweringOn': states.POWER_OFF
|
||||
}
|
||||
|
||||
SET_POWER_STATE_MAP = {
|
||||
states.POWER_ON: POWER_ON,
|
||||
states.POWER_OFF: POWER_OFF,
|
||||
states.REBOOT: REBOOT,
|
||||
states.SOFT_REBOOT: SOFT_REBOOT,
|
||||
states.SOFT_POWER_OFF: SOFT_POWER_OFF
|
||||
}
|
||||
|
||||
|
||||
class OneViewPower(base.PowerInterface):
|
||||
|
||||
# NOTE(TheJulia): Marking as unsupported as 3rd party CI was taken down
|
||||
# shortly before the beginning of the Rocky cycle, and no replies have
|
||||
# indicated that 3rd party CI will be re-established nor visible
|
||||
# actions observed regarding re-establishing 3rd party CI.
|
||||
# TODO(TheJulia): This should be expected to be removed in Stein.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return deploy_utils.get_properties()
|
||||
|
||||
@METRICS.timer('OneViewPower.validate')
|
||||
def validate(self, task):
|
||||
"""Checks required info on 'driver_info' and validates node for OneView
|
||||
|
||||
Validates whether the 'oneview_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, and if
|
||||
the server hardware attributes (ram, memory, vcpus count) are
|
||||
consistent with OneView. It validates if the node is being used by
|
||||
Oneview.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: MissingParameterValue if a required parameter is missing.
|
||||
:raises: InvalidParameterValue if parameters set are inconsistent with
|
||||
resources in OneView
|
||||
:raises: InvalidParameterValue if the node in use by OneView.
|
||||
:raises: OneViewError if not possible to get OneView's information
|
||||
for the given node, if not possible to retrieve Server
|
||||
Hardware from OneView.
|
||||
"""
|
||||
common.verify_node_info(task.node)
|
||||
|
||||
try:
|
||||
common.validate_oneview_resources_compatibility(task)
|
||||
if deploy_utils.is_node_in_use_by_oneview(task.node):
|
||||
raise exception.InvalidParameterValue(
|
||||
_("Node %s is in use by OneView.") % task.node.uuid)
|
||||
except exception.OneViewError as oneview_exc:
|
||||
raise exception.InvalidParameterValue(oneview_exc)
|
||||
|
||||
@METRICS.timer('OneViewPower.get_power_state')
|
||||
def get_power_state(self, task):
|
||||
"""Gets the current power state.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:returns: one of :mod:`ironic.common.states` POWER_OFF,
|
||||
POWER_ON or ERROR.
|
||||
:raises: OneViewError if fails to retrieve power state of OneView
|
||||
resource
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
server_hardware = task.node.driver_info.get('server_hardware_uri')
|
||||
try:
|
||||
server_hardware = oneview_client.server_hardware.get(
|
||||
server_hardware)
|
||||
except client_exception.HPOneViewException as exc:
|
||||
LOG.error(
|
||||
"Error getting power state for node %(node)s. Error:"
|
||||
"%(error)s",
|
||||
{'node': task.node.uuid, 'error': exc}
|
||||
)
|
||||
raise exception.OneViewError(error=exc)
|
||||
else:
|
||||
power_state = server_hardware.get('powerState')
|
||||
return GET_POWER_STATE_MAP.get(power_state)
|
||||
|
||||
@METRICS.timer('OneViewPower.set_power_state')
|
||||
@task_manager.require_exclusive_lock
|
||||
def set_power_state(self, task, power_state, timeout=None):
|
||||
"""Turn the current power state on or off.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:param power_state: The desired power state POWER_ON, POWER_OFF or
|
||||
REBOOT from :mod:`ironic.common.states`.
|
||||
:param timeout: timeout (in seconds) positive integer (> 0) for any
|
||||
power state. ``None`` indicates the default timeout.
|
||||
:raises: InvalidParameterValue if an invalid power state was specified.
|
||||
:raises: PowerStateFailure if the power couldn't be set to power_state.
|
||||
:raises: OneViewError if OneView fails setting the power state.
|
||||
"""
|
||||
oneview_client = common.get_hponeview_client()
|
||||
|
||||
if deploy_utils.is_node_in_use_by_oneview(task.node):
|
||||
raise exception.PowerStateFailure(_(
|
||||
"Cannot set power state '%(power_state)s' to node %(node)s. "
|
||||
"The node is in use by OneView.") %
|
||||
{'power_state': power_state,
|
||||
'node': task.node.uuid})
|
||||
|
||||
if power_state not in SET_POWER_STATE_MAP:
|
||||
raise exception.InvalidParameterValue(
|
||||
_("set_power_state called with invalid power state %(state)s "
|
||||
"on node: %(node)s") % {
|
||||
'state': power_state,
|
||||
'node': task.node.uuid
|
||||
})
|
||||
|
||||
LOG.debug('Setting power state of node %(node_uuid)s to '
|
||||
'%(power_state)s',
|
||||
{'node_uuid': task.node.uuid, 'power_state': power_state})
|
||||
|
||||
server_hardware = task.node.driver_info.get('server_hardware_uri')
|
||||
timeout = -1 if timeout is None else timeout
|
||||
|
||||
try:
|
||||
if power_state == states.POWER_ON:
|
||||
management.set_boot_device(task)
|
||||
oneview_client.server_hardware.update_power_state(
|
||||
SET_POWER_STATE_MAP.get(power_state),
|
||||
server_hardware, timeout=timeout)
|
||||
elif (power_state == states.REBOOT
|
||||
or power_state == states.SOFT_REBOOT):
|
||||
power_off_mode = (states.POWER_OFF
|
||||
if power_state == states.REBOOT
|
||||
else states.SOFT_POWER_OFF)
|
||||
|
||||
oneview_client.server_hardware.update_power_state(
|
||||
SET_POWER_STATE_MAP.get(power_off_mode),
|
||||
server_hardware, timeout=timeout)
|
||||
management.set_boot_device(task)
|
||||
oneview_client.server_hardware.update_power_state(
|
||||
SET_POWER_STATE_MAP.get(states.POWER_ON),
|
||||
server_hardware, timeout=timeout)
|
||||
else:
|
||||
oneview_client.server_hardware.update_power_state(
|
||||
SET_POWER_STATE_MAP.get(power_state),
|
||||
server_hardware, timeout=timeout)
|
||||
except client_exception.HPOneViewException as exc:
|
||||
raise exception.OneViewError(
|
||||
_("Failed to setting power state on node: %(node)s. "
|
||||
"Error: %(error)s.") % {
|
||||
'node': task.node.uuid,
|
||||
'error': exc
|
||||
})
|
||||
|
||||
@METRICS.timer('OneViewPower.reboot')
|
||||
@task_manager.require_exclusive_lock
|
||||
def reboot(self, task, timeout=None):
|
||||
"""Reboot the node.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:param timeout: timeout (in seconds) positive integer (> 0) for any
|
||||
power state. ``None`` indicates to use default timeout.
|
||||
:raises: PowerStateFailure if the final state of the node is not
|
||||
POWER_ON.
|
||||
"""
|
||||
current_power_state = self.get_power_state(task)
|
||||
if current_power_state == states.POWER_ON:
|
||||
self.set_power_state(task, states.REBOOT, timeout=timeout)
|
||||
else:
|
||||
self.set_power_state(task, states.POWER_ON, timeout=timeout)
|
||||
|
||||
@METRICS.timer('OneViewPower.get_supported_power_states')
|
||||
def get_supported_power_states(self, task):
|
||||
"""Get a list of the supported power states.
|
||||
|
||||
:param task: A TaskManager instance containing the node to act on.
|
||||
Currently not used.
|
||||
:returns: A list with the supported power states defined
|
||||
in :mod:`ironic.common.states`.
|
||||
"""
|
||||
return [states.POWER_ON, states.POWER_OFF, states.REBOOT,
|
||||
states.SOFT_REBOOT, states.SOFT_POWER_OFF]
|
@ -1,58 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
OneView hardware type.
|
||||
"""
|
||||
|
||||
from ironic.drivers import generic
|
||||
from ironic.drivers.modules import noop
|
||||
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
|
||||
|
||||
|
||||
class OneViewHardware(generic.GenericHardware):
|
||||
"""OneView hardware type.
|
||||
|
||||
OneView hardware type is targeted for OneView
|
||||
"""
|
||||
# NOTE(TheJulia): Marking as unsupported as 3rd party CI was taken down
|
||||
# shortly before the beginning of the Rocky cycle, and no replies have
|
||||
# indicated that 3rd party CI will be re-established nor visible
|
||||
# actions observed regarding re-establishing 3rd party CI.
|
||||
# TODO(TheJulia): This should be expected to be removed in Stein.
|
||||
supported = False
|
||||
|
||||
@property
|
||||
def supported_deploy_interfaces(self):
|
||||
"""List of supported deploy interfaces."""
|
||||
return [deploy.OneViewIscsiDeploy, deploy.OneViewAgentDeploy]
|
||||
|
||||
@property
|
||||
def supported_inspect_interfaces(self):
|
||||
"""List of supported inspect interfaces."""
|
||||
return [inspect.OneViewInspect, noop.NoInspect]
|
||||
|
||||
@property
|
||||
def supported_management_interfaces(self):
|
||||
"""List of supported management interfaces."""
|
||||
return [management.OneViewManagement]
|
||||
|
||||
@property
|
||||
def supported_power_interfaces(self):
|
||||
"""List of supported power interfaces."""
|
||||
return [power.OneViewPower]
|
@ -423,24 +423,6 @@ def get_test_cimc_info():
|
||||
}
|
||||
|
||||
|
||||
def get_test_oneview_properties():
|
||||
return {
|
||||
"cpu_arch": "x86_64",
|
||||
"cpus": "8",
|
||||
"local_gb": "10",
|
||||
"memory_mb": "4096",
|
||||
"capabilities": ("server_hardware_type_uri:fake_sht_uri,"
|
||||
"enclosure_group_uri:fake_eg_uri,"
|
||||
"server_profile_template_uri:fake_spt_uri"),
|
||||
}
|
||||
|
||||
|
||||
def get_test_oneview_driver_info():
|
||||
return {
|
||||
'server_hardware_uri': 'fake_sh_uri',
|
||||
}
|
||||
|
||||
|
||||
def get_test_redfish_info():
|
||||
return {
|
||||
"redfish_address": "https://example.com",
|
||||
|
@ -1,406 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 exception
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.oneview import common
|
||||
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
|
||||
|
||||
hponeview_client = importutils.try_import('hpOneView.oneview_client')
|
||||
|
||||
|
||||
class BaseOneViewTest(db_base.DbTestCase):
|
||||
|
||||
deploy_interface = None
|
||||
|
||||
def setUp(self):
|
||||
super(BaseOneViewTest, self).setUp()
|
||||
self.config(enabled_hardware_types=['oneview', 'fake-hardware'],
|
||||
enabled_deploy_interfaces=['oneview-direct',
|
||||
'oneview-iscsi', 'fake'],
|
||||
enabled_management_interfaces=['oneview', 'fake'],
|
||||
enabled_inspect_interfaces=['oneview', 'fake',
|
||||
'no-inspect'],
|
||||
enabled_power_interfaces=['oneview', 'fake'])
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='oneview',
|
||||
deploy_interface=self.deploy_interface,
|
||||
properties=db_utils.get_test_oneview_properties(),
|
||||
driver_info=db_utils.get_test_oneview_driver_info(),
|
||||
)
|
||||
|
||||
|
||||
class OneViewCommonTestCase(BaseOneViewTest):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewCommonTestCase, 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')
|
||||
self.config(tls_cacert_file='ca_file', group='oneview')
|
||||
self.config(allow_insecure_connections=False, group='oneview')
|
||||
|
||||
def test_prepare_manager_url(self):
|
||||
self.assertEqual(
|
||||
common.prepare_manager_url("https://1.2.3.4/"), "1.2.3.4")
|
||||
self.assertEqual(
|
||||
common.prepare_manager_url("http://oneview"), "oneview")
|
||||
self.assertEqual(
|
||||
common.prepare_manager_url("http://oneview:8080"), "oneview:8080")
|
||||
self.assertEqual(
|
||||
common.prepare_manager_url("http://oneview/something"), "oneview")
|
||||
self.assertEqual(
|
||||
common.prepare_manager_url("oneview/something"), "oneview")
|
||||
self.assertEqual(
|
||||
common.prepare_manager_url("oneview"), "oneview")
|
||||
|
||||
@mock.patch.object(hponeview_client, 'OneViewClient', autospec=True)
|
||||
def test_get_hponeview_client(self, mock_hponeview_client):
|
||||
common.get_hponeview_client()
|
||||
mock_hponeview_client.assert_called_once_with(self.config)
|
||||
|
||||
def test_get_hponeview_client_insecure_false(self):
|
||||
self.config(tls_cacert_file=None, group='oneview')
|
||||
self.assertRaises(exception.OneViewError, common.get_hponeview_client)
|
||||
|
||||
@mock.patch.object(hponeview_client, 'OneViewClient', autospec=True)
|
||||
def test_get_hponeview_client_insecure_cafile(self, mock_oneview):
|
||||
self.config(allow_insecure_connections=True, group='oneview')
|
||||
credentials = {
|
||||
"ip": 'https://1.2.3.4',
|
||||
"credentials": {
|
||||
"userName": 'user',
|
||||
"password": 'password'
|
||||
},
|
||||
"ssl_certificate": None
|
||||
}
|
||||
mock_oneview.assert_called_once_with(credentials)
|
||||
|
||||
def test_get_ilo_access(self):
|
||||
url = ("hplocons://addr=1.2.3.4&sessionkey"
|
||||
"=a79659e3b3b7c8209c901ac3509a6719")
|
||||
remote_console = {'remoteConsoleUrl': url}
|
||||
host_ip, token = common._get_ilo_access(remote_console)
|
||||
self.assertEqual(host_ip, "1.2.3.4")
|
||||
self.assertEqual(token, "a79659e3b3b7c8209c901ac3509a6719")
|
||||
|
||||
def test_verify_node_info(self):
|
||||
common.verify_node_info(self.node)
|
||||
|
||||
def test_verify_node_info_missing_node_properties(self):
|
||||
self.node.properties = {
|
||||
"cpu_arch": "x86_64",
|
||||
"cpus": "8",
|
||||
"local_gb": "10",
|
||||
"memory_mb": "4096",
|
||||
"capabilities": ("enclosure_group_uri:fake_eg_uri,"
|
||||
"server_profile_template_uri:fake_spt_uri")
|
||||
}
|
||||
with self.assertRaisesRegex(exception.MissingParameterValue,
|
||||
"server_hardware_type_uri"):
|
||||
common.verify_node_info(self.node)
|
||||
|
||||
def test_verify_node_info_missing_node_driver_info(self):
|
||||
self.node.driver_info = {}
|
||||
|
||||
with self.assertRaisesRegex(exception.MissingParameterValue,
|
||||
"server_hardware_uri"):
|
||||
common.verify_node_info(self.node)
|
||||
|
||||
def test_verify_node_info_missing_spt(self):
|
||||
properties = db_utils.get_test_oneview_properties()
|
||||
properties["capabilities"] = ("server_hardware_type_uri:fake_sht_uri,"
|
||||
"enclosure_group_uri:fake_eg_uri")
|
||||
|
||||
self.node.properties = properties
|
||||
with self.assertRaisesRegex(exception.MissingParameterValue,
|
||||
"server_profile_template_uri"):
|
||||
common.verify_node_info(self.node)
|
||||
|
||||
def test_verify_node_info_missing_sh(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"] = (
|
||||
"server_hardware_type_uri:fake_sht_uri,"
|
||||
"enclosure_group_uri:fake_eg_uri,"
|
||||
"server_profile_template_uri:fake_spt_uri"
|
||||
)
|
||||
|
||||
self.node.properties = properties
|
||||
self.node.driver_info = driver_info
|
||||
with self.assertRaisesRegex(exception.MissingParameterValue,
|
||||
"server_hardware_uri"):
|
||||
common.verify_node_info(self.node)
|
||||
|
||||
def test_verify_node_info_missing_sht(self):
|
||||
driver_info = db_utils.get_test_oneview_driver_info()
|
||||
properties = db_utils.get_test_oneview_properties()
|
||||
properties["capabilities"] = (
|
||||
"enclosure_group_uri:fake_eg_uri,"
|
||||
"server_profile_template_uri:fake_spt_uri"
|
||||
)
|
||||
|
||||
self.node.properties = properties
|
||||
self.node.driver_info = driver_info
|
||||
with self.assertRaisesRegex(exception.MissingParameterValue,
|
||||
"server_hardware_type_uri"):
|
||||
common.verify_node_info(self.node)
|
||||
|
||||
def test_get_oneview_info(self):
|
||||
complete_node = self.node
|
||||
expected_node_info = {
|
||||
'server_hardware_uri': 'fake_sh_uri',
|
||||
'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(
|
||||
expected_node_info,
|
||||
common.get_oneview_info(complete_node)
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
self.node.driver_info = driver_info
|
||||
self.node.properties = properties
|
||||
|
||||
incomplete_node = self.node
|
||||
expected_node_info = {
|
||||
'server_hardware_uri': 'fake_sh_uri',
|
||||
'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(
|
||||
expected_node_info,
|
||||
common.get_oneview_info(incomplete_node)
|
||||
)
|
||||
|
||||
def test_get_oneview_info_missing_sh(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"] = (
|
||||
"server_hardware_type_uri:fake_sht_uri,"
|
||||
"enclosure_group_uri:fake_eg_uri,"
|
||||
"server_profile_template_uri:fake_spt_uri"
|
||||
)
|
||||
|
||||
self.node.driver_info = driver_info
|
||||
self.node.properties = properties
|
||||
|
||||
incomplete_node = self.node
|
||||
expected_node_info = {
|
||||
'server_hardware_uri': None,
|
||||
'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(
|
||||
expected_node_info,
|
||||
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)
|
||||
|
||||
def test__verify_node_info(self):
|
||||
common._verify_node_info("properties",
|
||||
{"a": True,
|
||||
"b": False,
|
||||
"c": 0,
|
||||
"d": "something",
|
||||
"e": "somethingelse"},
|
||||
["a", "b", "c", "e"])
|
||||
|
||||
def test__verify_node_info_fails(self):
|
||||
self.assertRaises(
|
||||
exception.MissingParameterValue,
|
||||
common._verify_node_info,
|
||||
"properties",
|
||||
{"a": 1, "b": 2, "c": 3},
|
||||
["x"]
|
||||
)
|
||||
|
||||
def test__verify_node_info_missing_values_empty_string(self):
|
||||
with self.assertRaisesRegex(exception.MissingParameterValue,
|
||||
"'properties:a', 'properties:b'"):
|
||||
common._verify_node_info("properties",
|
||||
{"a": '', "b": None, "c": "something"},
|
||||
["a", "b", "c"])
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client', autospec=True)
|
||||
@mock.patch.object(common, '_validate_node_server_profile_template')
|
||||
@mock.patch.object(common, '_validate_node_server_hardware_type')
|
||||
@mock.patch.object(common, '_validate_node_enclosure_group')
|
||||
@mock.patch.object(common, '_validate_node_port_mac_server_hardware')
|
||||
@mock.patch.object(common, '_validate_server_profile_template_mac_type')
|
||||
def test_validate_oneview_resources_compatibility(
|
||||
self, mock_spt_mac_type, mock_port_mac_sh, mock_enclosure,
|
||||
mock_sh_type, mock_sp_template, mock_hponeview):
|
||||
"""Validate compatibility of resources.
|
||||
|
||||
1) Check _validate_node_server_profile_template method is called
|
||||
2) Check _validate_node_server_hardware_type method is called
|
||||
3) Check _validate_node_enclosure_group method is called
|
||||
4) Check _validate_node_port_mac_server_hardware method is called
|
||||
5) Check _validate_server_profile_template_mac_type method is called
|
||||
"""
|
||||
oneview_client = mock_hponeview()
|
||||
fake_port = db_utils.create_test_port()
|
||||
fake_port.address = 'AA:BB:CC:DD:EE'
|
||||
fake_device = {'physicalPorts': [
|
||||
{'type': 'Ethernet',
|
||||
'virtualPorts': [
|
||||
{'portFunction': 'a',
|
||||
'mac': 'AA:BB:CC:DD:EE'}
|
||||
]}
|
||||
]}
|
||||
fake_spt = {
|
||||
'serverHardwareTypeUri': 'fake_sht_uri',
|
||||
'enclosureGroupUri': 'fake_eg_uri',
|
||||
'macType': 'Physical',
|
||||
'boot': {'manageBoot': True}
|
||||
}
|
||||
fake_sh = {
|
||||
'serverHardwareTypeUri': 'fake_sht_uri',
|
||||
'serverGroupUri': 'fake_eg_uri',
|
||||
'processorCoreCount': 4,
|
||||
'processorCount': 2,
|
||||
'memoryMb': 4096,
|
||||
'portMap': {'deviceSlots': [fake_device]}
|
||||
}
|
||||
oneview_client.server_profile_templates.get.return_value = fake_spt
|
||||
oneview_client.server_hardware.get.return_value = fake_sh
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.ports = [fake_port]
|
||||
common.validate_oneview_resources_compatibility(task)
|
||||
self.assertTrue(mock_sp_template.called)
|
||||
self.assertTrue(mock_sh_type.called)
|
||||
self.assertTrue(mock_enclosure.called)
|
||||
self.assertTrue(mock_port_mac_sh.called)
|
||||
self.assertTrue(mock_spt_mac_type.called)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client', autospec=True)
|
||||
def test__validate_server_profile_template_mac_type_virtual(
|
||||
self, mock_hponeview):
|
||||
oneview_client = mock_hponeview()
|
||||
fake_spt = {'macType': 'Virtual'}
|
||||
oneview_client.server_hardware.get.return_value = fake_spt
|
||||
oneview_info = {'server_profile_template_uri': 'fake_uri'}
|
||||
|
||||
self.assertRaises(exception.OneViewError,
|
||||
common._validate_server_profile_template_mac_type,
|
||||
oneview_client, oneview_info)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client', autospec=True)
|
||||
def test__validate_node_port_mac_server_hardware_invalid(
|
||||
self, mock_hponeview):
|
||||
oneview_client = mock_hponeview()
|
||||
fake_device = {
|
||||
'physicalPorts': [
|
||||
{'type': 'notEthernet',
|
||||
'mac': '00:11:22:33:44',
|
||||
'virtualPorts': [{
|
||||
'portFunction': 'a',
|
||||
'mac': 'AA:BB:CC:DD:EE'}]},
|
||||
{'type': 'Ethernet',
|
||||
'mac': '11:22:33:44:55',
|
||||
'virtualPorts': [{
|
||||
'portFunction': 'a',
|
||||
'mac': 'BB:CC:DD:EE:FF'}]}]}
|
||||
fake_sh = {'portMap': {'deviceSlots': [fake_device]}}
|
||||
fake_port = db_utils.create_test_port(address='AA:BB:CC:DD:EE')
|
||||
oneview_client.server_hardware.get.return_value = fake_sh
|
||||
oneview_info = db_utils.get_test_oneview_driver_info()
|
||||
|
||||
self.assertRaises(exception.OneViewError,
|
||||
common._validate_node_port_mac_server_hardware,
|
||||
oneview_client, oneview_info, [fake_port])
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client', autospec=True)
|
||||
def test__validate_node_enclosure_group_invalid(self, mock_hponeview):
|
||||
oneview_client = mock_hponeview()
|
||||
fake_sh = {'serverGroupUri': 'invalid_fake_eg_uri'}
|
||||
oneview_client.server_hardware.get.return_value = fake_sh
|
||||
oneview_info = {'server_hardware_uri': 'fake_sh_uri',
|
||||
'enclosure_group_uri': 'fake_eg_uri'}
|
||||
|
||||
self.assertRaises(exception.OneViewError,
|
||||
common._validate_node_enclosure_group,
|
||||
oneview_client, oneview_info)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client', autospec=True)
|
||||
def test__validate_node_server_hardware_type(self, mock_hponeview):
|
||||
oneview_client = mock_hponeview()
|
||||
fake_sh = {'serverHardwareTypeUri': 'invalid_fake_sh_uri'}
|
||||
oneview_client.server_hardware.get.return_value = fake_sh
|
||||
oneview_info = {'server_hardware_uri': 'fake_sh_uri',
|
||||
'server_hardware_type_uri': 'fake_sht_uri'}
|
||||
|
||||
self.assertRaises(exception.OneViewError,
|
||||
common._validate_node_server_hardware_type,
|
||||
oneview_client, oneview_info)
|
||||
|
||||
def test__validate_server_profile_template_manage_boot_false(self):
|
||||
fake_spt = {'boot': {'manageBoot': False}}
|
||||
self.assertRaises(exception.OneViewError,
|
||||
common._validate_server_profile_template_manage_boot,
|
||||
fake_spt)
|
||||
|
||||
def test__validate_spt_enclosure_group_invalid(self):
|
||||
fake_spt = {'enclosureGroupUri': 'fake_eg_uri'}
|
||||
fake_sh = {'serverGroupUri': 'invalid_fake_eg_uri'}
|
||||
self.assertRaises(exception.OneViewError,
|
||||
common._validate_spt_enclosure_group,
|
||||
fake_spt, fake_sh)
|
||||
|
||||
def test__validate_server_profile_template_server_hardware_type(self):
|
||||
fake_spt = {'serverHardwareTypeUri': 'fake_sht_uri'}
|
||||
fake_sh = {'serverHardwareTypeUri': 'invalid_fake_sht_uri'}
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
common._validate_server_profile_template_server_hardware_type,
|
||||
fake_spt, fake_sh)
|
@ -1,456 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conf import CONF
|
||||
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 deploy_utils
|
||||
from ironic.tests.unit.drivers.modules.oneview import test_common
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
METHODS = ['iter_nodes', 'update_node', 'do_provisioning_action']
|
||||
|
||||
oneview_error = common.SERVER_HARDWARE_ALLOCATION_ERROR
|
||||
maintenance_reason = common.NODE_IN_USE_BY_ONEVIEW
|
||||
|
||||
driver_internal_info = {'oneview_error': oneview_error}
|
||||
nodes_taken_by_oneview = [(1, 'oneview', '')]
|
||||
nodes_freed_by_oneview = [(1, 'oneview', '', maintenance_reason)]
|
||||
nodes_taken_on_cleanfail = [(1, 'oneview', '', driver_internal_info)]
|
||||
nodes_taken_on_cleanfail_no_info = [(1, 'oneview', '', {})]
|
||||
|
||||
GET_POWER_STATE_RETRIES = 5
|
||||
|
||||
|
||||
def _setup_node_in_available_state(node):
|
||||
node.provision_state = states.AVAILABLE
|
||||
node.maintenance = False
|
||||
node.maintenance_reason = None
|
||||
node.save()
|
||||
|
||||
|
||||
def _setup_node_in_manageable_state(node):
|
||||
node.provision_state = states.MANAGEABLE
|
||||
node.maintenance = True
|
||||
node.maintenance_reason = common.NODE_IN_USE_BY_ONEVIEW
|
||||
node.save()
|
||||
|
||||
|
||||
def _setup_node_in_cleanfailed_state_with_oneview_error(node):
|
||||
node.provision_state = states.CLEANFAIL
|
||||
node.maintenance = False
|
||||
node.maintenance_reason = None
|
||||
driver_internal_info = node.driver_internal_info
|
||||
oneview_error = common.SERVER_HARDWARE_ALLOCATION_ERROR
|
||||
driver_internal_info['oneview_error'] = oneview_error
|
||||
node.driver_internal_info = driver_internal_info
|
||||
node.save()
|
||||
|
||||
|
||||
def _setup_node_in_cleanfailed_state_without_oneview_error(node):
|
||||
node.provision_state = states.CLEANFAIL
|
||||
node.maintenance = False
|
||||
node.maintenance_reason = None
|
||||
node.save()
|
||||
|
||||
|
||||
class OneViewDriverDeploy(deploy.OneViewPeriodicTasks):
|
||||
oneview_driver = 'oneview'
|
||||
|
||||
|
||||
@mock.patch('ironic.objects.Node', spec_set=True, autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'is_node_in_use_by_oneview')
|
||||
class OneViewPeriodicTasks(test_common.BaseOneViewTest):
|
||||
|
||||
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')
|
||||
|
||||
self.deploy = OneViewDriverDeploy()
|
||||
self.os_primary = mock.MagicMock(spec=METHODS)
|
||||
|
||||
def test_node_manageable_maintenance_when_in_use_by_oneview(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_available_state(self.node)
|
||||
self.os_primary.iter_nodes.return_value = nodes_taken_by_oneview
|
||||
mock_is_node_in_use_by_oneview.return_value = True
|
||||
self.deploy._periodic_check_nodes_taken_by_oneview(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
mock_is_node_in_use_by_oneview.assert_called_once_with(self.node)
|
||||
self.assertTrue(self.os_primary.update_node.called)
|
||||
self.assertTrue(self.os_primary.do_provisioning_action.called)
|
||||
self.assertTrue(self.node.maintenance)
|
||||
self.assertEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
|
||||
def test_node_stay_available_when_not_in_use_by_oneview(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_available_state(self.node)
|
||||
mock_node_get.return_value = self.node
|
||||
mock_is_node_in_use_by_oneview.return_value = False
|
||||
self.os_primary.iter_nodes.return_value = nodes_taken_by_oneview
|
||||
self.deploy._periodic_check_nodes_taken_by_oneview(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
mock_is_node_in_use_by_oneview.assert_called_once_with(self.node)
|
||||
self.assertFalse(self.os_primary.update_node.called)
|
||||
self.assertFalse(self.os_primary.do_provisioning_action.called)
|
||||
self.assertFalse(self.node.maintenance)
|
||||
self.assertIsNone(self.node.maintenance_reason)
|
||||
|
||||
def test_node_stay_available_when_raise_exception(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_available_state(self.node)
|
||||
side_effect = exception.OneViewError('boom')
|
||||
mock_is_node_in_use_by_oneview.side_effect = side_effect
|
||||
self.os_primary.iter_nodes.return_value = nodes_taken_by_oneview
|
||||
self.deploy._periodic_check_nodes_taken_by_oneview(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
mock_is_node_in_use_by_oneview.assert_called_once_with(self.node)
|
||||
self.assertFalse(self.os_primary.update_node.called)
|
||||
self.assertFalse(self.os_primary.do_provisioning_action.called)
|
||||
self.assertFalse(self.node.maintenance)
|
||||
self.assertNotEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
|
||||
def test_node_available_when_not_in_use_by_oneview(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_manageable_state(self.node)
|
||||
self.os_primary.iter_nodes.return_value = nodes_freed_by_oneview
|
||||
mock_is_node_in_use_by_oneview.return_value = False
|
||||
self.deploy._periodic_check_nodes_freed_by_oneview(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
mock_is_node_in_use_by_oneview.assert_called_once_with(self.node)
|
||||
self.assertTrue(self.os_primary.update_node.called)
|
||||
self.assertTrue(self.os_primary.do_provisioning_action.called)
|
||||
self.assertFalse(self.node.maintenance)
|
||||
self.assertIsNone(self.node.maintenance_reason)
|
||||
|
||||
def test_node_stay_manageable_when_in_use_by_oneview(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_manageable_state(self.node)
|
||||
mock_is_node_in_use_by_oneview.return_value = True
|
||||
self.os_primary.iter_nodes.return_value = nodes_freed_by_oneview
|
||||
self.deploy._periodic_check_nodes_freed_by_oneview(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
mock_is_node_in_use_by_oneview.assert_called_once_with(self.node)
|
||||
self.assertFalse(self.os_primary.update_node.called)
|
||||
self.assertFalse(self.os_primary.do_provisioning_action.called)
|
||||
self.assertTrue(self.node.maintenance)
|
||||
self.assertEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
|
||||
def test_node_stay_manageable_maintenance_when_raise_exception(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_manageable_state(self.node)
|
||||
side_effect = exception.OneViewError('boom')
|
||||
mock_is_node_in_use_by_oneview.side_effect = side_effect
|
||||
self.os_primary.iter_nodes.return_value = nodes_freed_by_oneview
|
||||
self.deploy._periodic_check_nodes_freed_by_oneview(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
mock_is_node_in_use_by_oneview.assert_called_once_with(self.node)
|
||||
self.assertFalse(self.os_primary.update_node.called)
|
||||
self.assertFalse(self.os_primary.do_provisioning_action.called)
|
||||
self.assertTrue(self.node.maintenance)
|
||||
self.assertEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
|
||||
def test_node_manageable_maintenance_when_oneview_error(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_cleanfailed_state_with_oneview_error(self.node)
|
||||
self.os_primary.iter_nodes.return_value = nodes_taken_on_cleanfail
|
||||
self.deploy._periodic_check_nodes_taken_on_cleanfail(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
self.assertTrue(self.os_primary.update_node.called)
|
||||
self.assertTrue(self.os_primary.do_provisioning_action.called)
|
||||
self.assertTrue(self.node.maintenance)
|
||||
self.assertEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
self.assertNotIn('oneview_error', self.node.driver_internal_info)
|
||||
|
||||
def test_node_stay_clean_failed_when_no_oneview_error(
|
||||
self, mock_is_node_in_use_by_oneview, mock_node_get
|
||||
):
|
||||
mock_node_get.get.return_value = self.node
|
||||
_setup_node_in_cleanfailed_state_without_oneview_error(self.node)
|
||||
self.os_primary.iter_nodes.return_value = (
|
||||
nodes_taken_on_cleanfail_no_info)
|
||||
self.deploy._periodic_check_nodes_taken_on_cleanfail(
|
||||
self.os_primary, self.context
|
||||
)
|
||||
self.assertFalse(self.os_primary.update_node.called)
|
||||
self.assertFalse(self.os_primary.do_provisioning_action.called)
|
||||
self.assertFalse(self.node.maintenance)
|
||||
self.assertNotEqual(common.NODE_IN_USE_BY_ONEVIEW,
|
||||
self.node.maintenance_reason)
|
||||
self.assertNotIn('oneview_error', self.node.driver_internal_info)
|
||||
|
||||
|
||||
class OneViewIscsiDeployTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
deploy_interface = 'oneview-iscsi'
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewIscsiDeployTestCase, 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')
|
||||
|
||||
self.port = obj_utils.create_test_port(self.context,
|
||||
node_id=self.node.id)
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = common.COMMON_PROPERTIES
|
||||
self.assertEqual(expected,
|
||||
deploy.OneViewIscsiDeploy().get_properties())
|
||||
|
||||
@mock.patch.object(common, 'validate_oneview_resources_compatibility',
|
||||
spect_set=True, autospec=True)
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'validate',
|
||||
spec_set=True, autospec=True)
|
||||
def test_validate(
|
||||
self, iscsi_deploy_validate_mock, mock_validate):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
self.assertTrue(mock_validate.called)
|
||||
iscsi_deploy_validate_mock.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic')
|
||||
def test_prepare(self, allocate_server_hardware_mock,
|
||||
iscsi_deploy_prepare_mock):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
iscsi_deploy_prepare_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertTrue(allocate_server_hardware_mock.called)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare',
|
||||
spec_set=True, autospec=True)
|
||||
def test_prepare_active_node(self, iscsi_deploy_prepare_mock):
|
||||
"""Ensure nodes in running states are not inadvertently changed"""
|
||||
test_states = list(states.STABLE_STATES)
|
||||
test_states.extend([states.CLEANING,
|
||||
states.CLEANWAIT,
|
||||
states.INSPECTING])
|
||||
for state in test_states:
|
||||
self.node.provision_state = state
|
||||
self.node.save()
|
||||
iscsi_deploy_prepare_mock.reset_mock()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
iscsi_deploy_prepare_mock.assert_called_once_with(
|
||||
mock.ANY, task)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'deploy',
|
||||
spec_set=True, autospec=True)
|
||||
def test_deploy(self, iscsi_deploy_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.deploy(task)
|
||||
iscsi_deploy_mock.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True,
|
||||
autospec=True)
|
||||
def test_tear_down(self, iscsi_tear_down_mock):
|
||||
iscsi_tear_down_mock.return_value = states.DELETED
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = task.driver.deploy.tear_down(task)
|
||||
iscsi_tear_down_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertEqual(states.DELETED, returned_state)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'deallocate_server_hardware_from_ironic')
|
||||
def test_tear_down_with_automated_clean_disabled(
|
||||
self, deallocate_server_hardware_mock, iscsi_tear_down_mock):
|
||||
CONF.conductor.automated_clean = False
|
||||
iscsi_tear_down_mock.return_value = states.DELETED
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = task.driver.deploy.tear_down(task)
|
||||
iscsi_tear_down_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertEqual(states.DELETED, returned_state)
|
||||
self.assertTrue(deallocate_server_hardware_mock.called)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare_cleaning',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic')
|
||||
def test_prepare_cleaning(
|
||||
self, allocate_server_hardware_mock, iscsi_prep_clean_mock):
|
||||
iscsi_prep_clean_mock.return_value = states.CLEANWAIT
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
ret = task.driver.deploy.prepare_cleaning(task)
|
||||
self.assertEqual(states.CLEANWAIT, ret)
|
||||
iscsi_prep_clean_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertTrue(allocate_server_hardware_mock.called)
|
||||
|
||||
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down_cleaning',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'deallocate_server_hardware_from_ironic')
|
||||
def test_tear_down_cleaning(
|
||||
self, deallocate_server_hardware_mock, iscsi_tear_down_clean_mock):
|
||||
iscsi_tear_down_clean_mock.return_value = states.CLEANWAIT
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.tear_down_cleaning(task)
|
||||
iscsi_tear_down_clean_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertTrue(deallocate_server_hardware_mock.called)
|
||||
|
||||
|
||||
class OneViewAgentDeployTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
deploy_interface = 'oneview-direct'
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewAgentDeployTestCase, 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')
|
||||
|
||||
self.port = obj_utils.create_test_port(self.context,
|
||||
node_id=self.node.id)
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = common.COMMON_PROPERTIES
|
||||
self.assertEqual(expected,
|
||||
deploy.OneViewAgentDeploy().get_properties())
|
||||
|
||||
@mock.patch.object(common, 'validate_oneview_resources_compatibility',
|
||||
spect_set=True, autospec=True)
|
||||
@mock.patch.object(agent.AgentDeploy, 'validate',
|
||||
spec_set=True, autospec=True)
|
||||
def test_validate(
|
||||
self, agent_deploy_validate_mock, mock_validate):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
self.assertTrue(mock_validate.called)
|
||||
|
||||
@mock.patch.object(agent.AgentDeploy, 'prepare',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic')
|
||||
def test_prepare(
|
||||
self, allocate_server_hardware_mock, agent_deploy_prepare_mock):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
agent_deploy_prepare_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertTrue(allocate_server_hardware_mock.called)
|
||||
|
||||
@mock.patch.object(agent.AgentDeploy, 'prepare',
|
||||
spec_set=True, autospec=True)
|
||||
def test_prepare_active_node(self, agent_deploy_prepare_mock):
|
||||
"""Ensure nodes in running states are not inadvertently changed"""
|
||||
test_states = list(states.STABLE_STATES)
|
||||
test_states.extend([states.CLEANING,
|
||||
states.CLEANWAIT,
|
||||
states.INSPECTING])
|
||||
for state in test_states:
|
||||
self.node.provision_state = state
|
||||
self.node.save()
|
||||
agent_deploy_prepare_mock.reset_mock()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
agent_deploy_prepare_mock.assert_called_once_with(
|
||||
mock.ANY, task)
|
||||
|
||||
@mock.patch.object(agent.AgentDeploy, 'deploy',
|
||||
spec_set=True, autospec=True)
|
||||
def test_deploy(self, agent_deploy_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.deploy(task)
|
||||
agent_deploy_mock.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
@mock.patch.object(agent.AgentDeploy, 'tear_down', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'deallocate_server_hardware_from_ironic')
|
||||
def test_tear_down_with_automated_clean_disabled(
|
||||
self, deallocate_server_hardware_mock, agent_tear_down_mock):
|
||||
CONF.conductor.automated_clean = False
|
||||
agent_tear_down_mock.return_value = states.DELETED
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = task.driver.deploy.tear_down(task)
|
||||
agent_tear_down_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertEqual(states.DELETED, returned_state)
|
||||
self.assertTrue(deallocate_server_hardware_mock.called)
|
||||
|
||||
@mock.patch.object(agent.AgentDeploy, 'prepare_cleaning',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic')
|
||||
def test_prepare_cleaning(
|
||||
self, allocate_server_hardware_mock, agent_prep_clean_mock):
|
||||
agent_prep_clean_mock.return_value = states.CLEANWAIT
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
ret = task.driver.deploy.prepare_cleaning(task)
|
||||
self.assertEqual(states.CLEANWAIT, ret)
|
||||
agent_prep_clean_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertTrue(allocate_server_hardware_mock.called)
|
||||
|
||||
@mock.patch.object(agent.AgentDeploy, 'tear_down_cleaning',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'deallocate_server_hardware_from_ironic')
|
||||
def test_tear_down_cleaning(
|
||||
self, deallocate_server_hardware_mock, agent_tear_down_clean_mock):
|
||||
agent_tear_down_clean_mock.return_value = states.CLEANWAIT
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.tear_down_cleaning(task)
|
||||
agent_tear_down_clean_mock.assert_called_once_with(mock.ANY, task)
|
||||
self.assertTrue(deallocate_server_hardware_mock.called)
|
@ -1,327 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 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.drivers.modules.oneview import test_common
|
||||
|
||||
oneview_models = importutils.try_import('oneview_client.models')
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
class OneViewDeployUtilsTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
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')
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
deploy_utils.is_node_in_use_by_oneview = mock.Mock(return_value=False)
|
||||
deploy_utils.is_node_in_use_by_ironic = mock.Mock(return_value=True)
|
||||
|
||||
# Tests for prepare
|
||||
def test_prepare_node_is_in_use_by_oneview(self, mock_oneview_client):
|
||||
"""`prepare` behavior when the node has a Profile on OneView."""
|
||||
deploy_utils.is_node_in_use_by_oneview.return_value = True
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
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_save, mock_oneview_client):
|
||||
"""`prepare` behavior when the node is free from OneView standpoint."""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
deploy_utils.prepare(task)
|
||||
self.assertTrue(mock_save.called)
|
||||
|
||||
# Tests for tear_down
|
||||
def test_tear_down(self, mock_oneview_client):
|
||||
"""`tear_down` behavior when node already has Profile applied."""
|
||||
oneview_client = mock_oneview_client()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'/rest/server-profiles/1234556789'
|
||||
)
|
||||
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(oneview_client.server_profiles.delete.called)
|
||||
|
||||
# Tests for prepare_cleaning
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test_prepare_cleaning_when_node_does_not_have_sp_applied(
|
||||
self, mock_save, mock_oneview_client):
|
||||
"""`prepare_cleaning` behavior when node is free."""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertFalse(
|
||||
'applied_server_profile_uri' in task.node.driver_info
|
||||
)
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
self.assertTrue(
|
||||
'applied_server_profile_uri' in task.node.driver_info
|
||||
)
|
||||
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test_prepare_cleaning_when_node_has_sp_applied(
|
||||
self, mock_node_save, mock_oneview_client):
|
||||
"""`prepare_cleaning` behavior when node has Profile applied."""
|
||||
oneview_client = mock_oneview_client()
|
||||
oneview_client.server_hardware.get.return_value = {
|
||||
'serverProfileUri': 'same/sp_applied'
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'same/sp_applied'
|
||||
)
|
||||
deploy_utils.prepare_cleaning(task)
|
||||
self.assertFalse(mock_node_save.called)
|
||||
|
||||
def test_prepare_cleaning_node_is_in_use_by_oneview(
|
||||
self, mock_oneview_client):
|
||||
"""`prepare_cleaning` behavior when node has Profile on OneView."""
|
||||
deploy_utils.is_node_in_use_by_oneview.return_value = True
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.NodeCleaningFailure,
|
||||
deploy_utils.prepare_cleaning,
|
||||
task
|
||||
)
|
||||
|
||||
# Tests for tear_down_cleaning
|
||||
def test_tear_down_cleaning(self, mock_oneview_client):
|
||||
"""Check if Server Profile was deleted and its uri removed."""
|
||||
oneview_client = mock_oneview_client()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'/rest/server-profiles/1234556789'
|
||||
)
|
||||
self.assertTrue(
|
||||
'applied_server_profile_uri' in task.node.driver_info
|
||||
)
|
||||
deploy_utils.tear_down_cleaning(task)
|
||||
self.assertFalse(
|
||||
'applied_server_profile_uri' in task.node.driver_info
|
||||
)
|
||||
self.assertTrue(oneview_client.server_profiles.delete.called)
|
||||
|
||||
# Tests for is_node_in_use_by_oneview
|
||||
def test_is_node_in_use_by_oneview(self, mock_oneview_client):
|
||||
"""Node has a Server Profile applied by a third party user."""
|
||||
server_hardware = {
|
||||
'serverProfileUri': '/rest/server-profile/123456789'
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'/rest/server-profile/987654321'
|
||||
)
|
||||
self.assertTrue(
|
||||
deploy_utils._is_node_in_use(
|
||||
server_hardware,
|
||||
task.node.driver_info['applied_server_profile_uri'],
|
||||
by_oneview=True
|
||||
)
|
||||
)
|
||||
|
||||
def test_is_node_in_use_by_oneview_no_server_profile(
|
||||
self, mock_oneview_client):
|
||||
"""Node has no Server Profile."""
|
||||
server_hardware = {'serverProfileUri': None}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'/rest/server-profile/123456789'
|
||||
)
|
||||
self.assertFalse(
|
||||
deploy_utils._is_node_in_use(
|
||||
server_hardware,
|
||||
task.node.driver_info['applied_server_profile_uri'],
|
||||
by_oneview=True
|
||||
)
|
||||
)
|
||||
|
||||
def test_is_node_in_use_by_oneview_same_server_profile_applied(
|
||||
self, mock_oneview_client):
|
||||
"""Check if node's Server Profile uri is the same applied by ironic."""
|
||||
server_hardware = {
|
||||
'serverProfileUri': '/rest/server-profile/123456789'
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'/rest/server-profile/123456789'
|
||||
)
|
||||
self.assertFalse(
|
||||
deploy_utils._is_node_in_use(
|
||||
server_hardware,
|
||||
task.node.driver_info['applied_server_profile_uri'],
|
||||
by_oneview=True
|
||||
)
|
||||
)
|
||||
|
||||
# Tests for is_node_in_use_by_ironic
|
||||
def test_is_node_in_use_by_ironic(self, mock_oneview_client):
|
||||
"""Node has a Server Profile applied by ironic."""
|
||||
server_hardware = {'serverProfileUri': 'same/applied_sp_uri/'}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'same/applied_sp_uri/'
|
||||
)
|
||||
self.assertTrue(
|
||||
deploy_utils._is_node_in_use(
|
||||
server_hardware,
|
||||
task.node.driver_info['applied_server_profile_uri'],
|
||||
by_oneview=False
|
||||
)
|
||||
)
|
||||
|
||||
def test_is_node_in_use_by_ironic_no_server_profile(
|
||||
self, mock_oneview_client):
|
||||
"""Node has no Server Profile."""
|
||||
server_hardware = {'serverProfileUri': None}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'/applied_sp_uri/'
|
||||
)
|
||||
self.assertFalse(
|
||||
deploy_utils._is_node_in_use(
|
||||
server_hardware,
|
||||
task.node.driver_info['applied_server_profile_uri'],
|
||||
by_oneview=False
|
||||
)
|
||||
)
|
||||
|
||||
def test__create_profile_from_template(self, mock_oneview_client):
|
||||
"""Check if the server_profile was created from template."""
|
||||
server_hardware_uri = "server_hardware/12456789"
|
||||
sp_template_uri = "server_profile_template_uri/13245798"
|
||||
oneview_client = mock_oneview_client()
|
||||
oneview_client.server_profile_templates.\
|
||||
get_new_profile.return_value = {}
|
||||
server_profile = {"name": "server_profile_name",
|
||||
"serverHardwareUri": server_hardware_uri,
|
||||
"serverProfileTemplateUri": ""}
|
||||
deploy_utils._create_profile_from_template(
|
||||
oneview_client,
|
||||
"server_profile_name",
|
||||
server_hardware_uri,
|
||||
sp_template_uri
|
||||
)
|
||||
oneview_client.server_profiles.create.assert_called_with(
|
||||
server_profile)
|
||||
|
||||
# Tests for _add_applied_server_profile_uri_field
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test__add_applied_server_profile_uri_field(
|
||||
self, save, mock_oneview_client):
|
||||
"""Check if applied_server_profile_uri was added to driver_info."""
|
||||
server_profile = {'uri': 'any/applied_sp_uri/'}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info.pop('applied_server_profile_uri', None)
|
||||
self.assertNotIn(
|
||||
'applied_server_profile_uri', task.node.driver_info
|
||||
)
|
||||
deploy_utils._add_applied_server_profile_uri_field(
|
||||
task.node,
|
||||
server_profile
|
||||
)
|
||||
self.assertIn('applied_server_profile_uri', task.node.driver_info)
|
||||
|
||||
# Tests for _del_applied_server_profile_uri_field
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test__del_applied_server_profile_uri_field(
|
||||
self, save, mock_oneview_client):
|
||||
"""Check if applied_server_profile_uri was removed from driver_info."""
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'any/applied_sp_uri/'
|
||||
)
|
||||
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_oneview_client):
|
||||
"""Check if a Profile was created and its uri is in driver_info."""
|
||||
oneview_client = mock_oneview_client()
|
||||
server_hardware = {'serverProfileUri': None}
|
||||
oneview_client.server_hardware.get.return_value = server_hardware
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
deploy_utils.allocate_server_hardware_to_ironic(
|
||||
task.node, 'serverProfileName'
|
||||
)
|
||||
self.assertTrue(mock_node_save.called)
|
||||
self.assertIn('applied_server_profile_uri', task.node.driver_info)
|
||||
|
||||
@mock.patch.object(objects.Node, 'save')
|
||||
def test_allocate_server_hardware_to_ironic_node_has_server_profile(
|
||||
self, mock_node_save, mock_oneview_client):
|
||||
"""Test 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.
|
||||
"""
|
||||
oneview_client = mock_oneview_client()
|
||||
server_hardware = {'serverProfileUri': None}
|
||||
oneview_client.server_hardware.get.return_value = server_hardware
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'any/applied_sp_uri/'
|
||||
)
|
||||
deploy_utils.allocate_server_hardware_to_ironic(
|
||||
task.node, 'serverProfileName'
|
||||
)
|
||||
self.assertTrue(mock_node_save.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_oneview_client):
|
||||
oneview_client = mock_oneview_client()
|
||||
server_hardware = {'serverProfileUri': 'any/applied_sp_uri/'}
|
||||
oneview_client.server_hardware.get.return_value = server_hardware
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['applied_server_profile_uri'] = (
|
||||
'any/applied_sp_uri/'
|
||||
)
|
||||
deploy_utils.deallocate_server_hardware_from_ironic(task)
|
||||
self.assertTrue(mock_node_save.called)
|
||||
self.assertTrue(
|
||||
'applied_server_profile_uri' not in task.node.driver_info
|
||||
)
|
@ -1,51 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 ov_common
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
from ironic.tests.unit.drivers.modules.oneview import test_common
|
||||
|
||||
|
||||
class OneViewInspectTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewInspectTestCase, self).setUp()
|
||||
self.config(enabled=True, group='inspector')
|
||||
self.config(manager_url='https://1.2.3.4', group='oneview')
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = deploy_utils.get_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(ov_common, 'validate_oneview_resources_compatibility',
|
||||
autospect=True)
|
||||
def test_validate(self, mock_validate):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.inspect.validate(task)
|
||||
self.assertTrue(mock_validate.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'allocate_server_hardware_to_ironic',
|
||||
autospect=True)
|
||||
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)
|
@ -1,365 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 oslo_utils import uuidutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.oneview import common
|
||||
from ironic.drivers.modules.oneview import deploy_utils
|
||||
from ironic.drivers.modules.oneview import management
|
||||
from ironic.tests.unit.drivers.modules.oneview import test_common
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
client_exception = importutils.try_import('hpOneView.exceptions')
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
class OneViewManagementDriverFunctionsTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewManagementDriverFunctionsTestCase, 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')
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_set_boot_device(
|
||||
self, mock_get_ilo_client, mock_get_ov_client):
|
||||
ilo_client = mock_get_ilo_client()
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'boot_device': boot_devices.PXE,
|
||||
'persistent': True}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
management.set_boot_device(task)
|
||||
self.assertFalse(ilo_client.called)
|
||||
patch = ilo_client.patch
|
||||
self.assertFalse(patch.called)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
self.assertNotIn('next_boot_device', driver_internal_info)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_set_boot_device_not_persistent(
|
||||
self, mock_get_ilo_client, mock_get_ov_client):
|
||||
ilo_client = mock_get_ilo_client()
|
||||
client = mock_get_ov_client()
|
||||
server_profile = {'boot': {'order':
|
||||
list(management.BOOT_DEVICE_MAP_ONEVIEW_REV)}}
|
||||
client.server_profiles.get.return_value = server_profile
|
||||
boot_device_map_ilo = management.BOOT_DEVICE_MAP_ILO
|
||||
boot_device = boot_device_map_ilo.get(boot_devices.PXE)
|
||||
body = {
|
||||
"Boot": {
|
||||
"BootSourceOverrideTarget": boot_device,
|
||||
"BootSourceOverrideEnabled": "Once"
|
||||
}
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_info = task.node.driver_info
|
||||
profile_uri = driver_info.get('applied_server_profile_uri')
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'boot_device': boot_devices.PXE,
|
||||
'persistent': False}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
management.set_boot_device(task)
|
||||
update = client.server_profiles.update
|
||||
update.assert_called_once_with(server_profile, profile_uri)
|
||||
patch = ilo_client.patch
|
||||
patch.assert_called_once_with(
|
||||
path=management.ILO_SYSTEM_PATH,
|
||||
body=body,
|
||||
headers=management.ILO_REQUEST_HEADERS
|
||||
)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
self.assertNotIn('next_boot_device', driver_internal_info)
|
||||
|
||||
def test_set_boot_device_invalid_device(self, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'boot_device': 'pixie-boots',
|
||||
'persistent': True}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
management.set_boot_device,
|
||||
task)
|
||||
self.assertFalse(client.set_boot_device.called)
|
||||
self.assertIn('next_boot_device', driver_internal_info)
|
||||
|
||||
def test_set_boot_device_fail_to_get_server_profile(
|
||||
self, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_profiles.get.side_effect = exc
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'device': 'disk',
|
||||
'persistent': True}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
management.set_boot_device,
|
||||
task
|
||||
)
|
||||
self.assertIn('next_boot_device', driver_internal_info)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_set_onetime_boot_persistent(
|
||||
self, mock_iloclient, mock_get_ov_client):
|
||||
ilo_client = mock_iloclient()
|
||||
driver_internal_info = self.node.driver_internal_info
|
||||
next_boot_device = {'device': 'disk', 'persistent': False}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
management.set_onetime_boot(task)
|
||||
self.assertFalse(ilo_client.called)
|
||||
self.assertFalse(ilo_client.patch.called)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_set_onetime_boot_not_persistent(
|
||||
self, mock_iloclient, mock_get_ov_client):
|
||||
ilo_client = mock_iloclient()
|
||||
boot_device = management.BOOT_DEVICE_MAP_ILO.get(boot_devices.DISK)
|
||||
body = {
|
||||
"Boot": {
|
||||
"BootSourceOverrideTarget": boot_device,
|
||||
"BootSourceOverrideEnabled": "Once"
|
||||
}
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'boot_device': 'disk', 'persistent': False}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
management.set_onetime_boot(task)
|
||||
self.assertTrue(mock_iloclient.called)
|
||||
ilo_client.patch.assert_called_once_with(
|
||||
path=management.ILO_SYSTEM_PATH,
|
||||
body=body,
|
||||
headers=management.ILO_REQUEST_HEADERS
|
||||
)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test__is_onetime_boot_true(
|
||||
self, mock_iloclient, mock_get_ov_client):
|
||||
|
||||
class RestResponse(object):
|
||||
@property
|
||||
def dict(self):
|
||||
return {'Boot': {'BootSourceOverrideEnabled': "Once"}}
|
||||
|
||||
ilo_client = mock_iloclient()
|
||||
ilo_client.get.return_value = RestResponse()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertTrue(management._is_onetime_boot(task))
|
||||
self.assertTrue(mock_iloclient.called)
|
||||
ilo_client.get.assert_called_with(
|
||||
path=management.ILO_SYSTEM_PATH,
|
||||
headers=management.ILO_REQUEST_HEADERS
|
||||
)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test__is_onetime_boot_false(
|
||||
self, mock_iloclient, mock_get_ov_client):
|
||||
|
||||
class RestResponse(object):
|
||||
@property
|
||||
def dict(self):
|
||||
return {'Boot': {'BootSourceOverrideEnabled': "Disabled"}}
|
||||
|
||||
ilo_client = mock_iloclient()
|
||||
ilo_client.get.return_value = RestResponse()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertFalse(management._is_onetime_boot(task))
|
||||
self.assertTrue(mock_iloclient.called)
|
||||
ilo_client.get.assert_called_with(
|
||||
path=management.ILO_SYSTEM_PATH,
|
||||
headers=management.ILO_REQUEST_HEADERS
|
||||
)
|
||||
|
||||
|
||||
class OneViewManagementDriverTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewManagementDriverTestCase, 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')
|
||||
self.config(tls_cacert_file='ca_file', group='oneview')
|
||||
self.config(allow_insecure_connections=False, group='oneview')
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'is_node_in_use_by_ironic',
|
||||
spect_set=True, autospec=True)
|
||||
@mock.patch.object(common, 'validate_oneview_resources_compatibility',
|
||||
spect_set=True, autospec=True)
|
||||
def test_validate(self, mock_validate, mock_ironic_node):
|
||||
mock_ironic_node.return_value = True
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.management.validate(task)
|
||||
self.assertTrue(mock_validate.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'is_node_in_use_by_ironic',
|
||||
spect_set=True, autospec=True)
|
||||
def test_validate_for_node_not_in_use_by_ironic(self, mock_ironic_node):
|
||||
mock_ironic_node.return_value = False
|
||||
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):
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, uuid=uuidutils.generate_uuid(),
|
||||
id=999, driver='oneview'
|
||||
)
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
task.driver.management.validate, task)
|
||||
|
||||
@mock.patch.object(common, 'validate_oneview_resources_compatibility')
|
||||
def test_validate_fail_exception(self, mock_validate):
|
||||
mock_validate.side_effect = exception.OneViewError('message')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.management.validate,
|
||||
task)
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = common.COMMON_PROPERTIES
|
||||
self.assertItemsEqual(expected,
|
||||
management.OneViewManagement().get_properties())
|
||||
|
||||
def test_set_boot_device_persistent_true(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.set_boot_device(
|
||||
task, boot_devices.PXE, True)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = driver_internal_info.get('next_boot_device')
|
||||
self.assertIn('next_boot_device', driver_internal_info)
|
||||
self.assertEqual(
|
||||
next_boot_device.get('boot_device'), boot_devices.PXE)
|
||||
self.assertTrue(next_boot_device.get('persistent'))
|
||||
|
||||
def test_set_boot_device_persistent_false(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.set_boot_device(
|
||||
task, boot_devices.PXE, False)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = driver_internal_info.get('next_boot_device')
|
||||
self.assertIn('next_boot_device', driver_internal_info)
|
||||
self.assertEqual(
|
||||
next_boot_device.get('boot_device'), boot_devices.PXE)
|
||||
self.assertFalse(next_boot_device.get('persistent'))
|
||||
|
||||
def test_set_boot_device_invalid_device(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.management.set_boot_device,
|
||||
task, 'unknown-device', False)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
self.assertNotIn('next_boot_device', driver_internal_info)
|
||||
|
||||
def test_get_supported_boot_devices(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
expected = [
|
||||
boot_devices.PXE, boot_devices.DISK, boot_devices.CDROM
|
||||
]
|
||||
self.assertItemsEqual(
|
||||
expected,
|
||||
task.driver.management.get_supported_boot_devices(task),
|
||||
)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_get_boot_device(self, mock_iloclient, mock_get_ov_client):
|
||||
ilo_client = mock_iloclient()
|
||||
oneview_client = mock_get_ov_client()
|
||||
device_mapping = management.BOOT_DEVICE_MAP_ONEVIEW.items()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
# For each known device on OneView, Ironic should return its
|
||||
# counterpart value
|
||||
for ironic_device, oneview_device in device_mapping:
|
||||
profile = {'boot': {'order': [oneview_device]}}
|
||||
oneview_client.server_profiles.get.return_value = profile
|
||||
expected = {'boot_device': ironic_device, 'persistent': True}
|
||||
response = management.OneViewManagement().get_boot_device(task)
|
||||
self.assertEqual(expected, response)
|
||||
self.assertTrue(oneview_client.server_profiles.get.called)
|
||||
self.assertTrue(ilo_client.get.called)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_get_boot_device_from_next_boot_device(self, mock_iloclient):
|
||||
ilo_client = mock_iloclient()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
next_boot_device = {'boot_device': boot_devices.DISK,
|
||||
'persistent': True}
|
||||
driver_internal_info['next_boot_device'] = next_boot_device
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
expected_response = {
|
||||
'boot_device': boot_devices.DISK,
|
||||
'persistent': True
|
||||
}
|
||||
response = management.OneViewManagement().get_boot_device(task)
|
||||
self.assertEqual(expected_response, response)
|
||||
self.assertFalse(ilo_client.get.called)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_get_boot_device_fail(self, mock_iloclient, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
ilo_client = mock_iloclient()
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_profiles.get.side_effect = exc
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
management.OneViewManagement().get_boot_device,
|
||||
task
|
||||
)
|
||||
self.assertTrue(client.server_profiles.get.called)
|
||||
self.assertFalse(ilo_client.get.called)
|
||||
|
||||
@mock.patch.object(common, 'get_ilorest_client')
|
||||
def test_get_boot_device_unknown_device(self, mock_iloclient):
|
||||
ilo_client = mock_iloclient()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.InvalidParameterValue,
|
||||
task.driver.management.get_boot_device,
|
||||
task
|
||||
)
|
||||
self.assertFalse(ilo_client.get.called)
|
||||
|
||||
def test_get_sensors_data_not_implemented(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
NotImplementedError,
|
||||
task.driver.management.get_sensors_data,
|
||||
task
|
||||
)
|
@ -1,308 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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 oslo_utils import uuidutils
|
||||
|
||||
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.drivers.modules.oneview import management
|
||||
from ironic.drivers.modules.oneview import power
|
||||
from ironic.tests.unit.drivers.modules.oneview import test_common
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
client_exception = importutils.try_import('hpOneView.exceptions')
|
||||
|
||||
|
||||
class OneViewPowerDriverTestCase(test_common.BaseOneViewTest):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewPowerDriverTestCase, 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')
|
||||
self.config(tls_cacert_file='ca_file', group='oneview')
|
||||
self.config(allow_insecure_connections=False, group='oneview')
|
||||
|
||||
self.info = common.get_oneview_info(self.node)
|
||||
deploy_utils.is_node_in_use_by_oneview = mock.Mock(return_value=False)
|
||||
|
||||
@mock.patch.object(common, 'validate_oneview_resources_compatibility')
|
||||
def test_validate(self, mock_validate):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.power.validate(task)
|
||||
self.assertTrue(mock_validate.called)
|
||||
|
||||
def test_validate_missing_parameter(self):
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, uuid=uuidutils.generate_uuid(),
|
||||
id=999, driver='oneview')
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.MissingParameterValue,
|
||||
task.driver.power.validate,
|
||||
task)
|
||||
|
||||
@mock.patch.object(common, 'validate_oneview_resources_compatibility')
|
||||
def test_validate_exception(self, mock_validate):
|
||||
mock_validate.side_effect = exception.OneViewError('message')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.InvalidParameterValue,
|
||||
task.driver.power.validate,
|
||||
task)
|
||||
|
||||
def test_validate_node_in_use_by_oneview(self):
|
||||
deploy_utils.is_node_in_use_by_oneview.return_value = True
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.InvalidParameterValue,
|
||||
task.driver.power.validate,
|
||||
task)
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = common.COMMON_PROPERTIES
|
||||
self.assertEqual(expected, power.OneViewPower().get_properties())
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
def test_get_power_state(self, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = {'powerState': 'On'}
|
||||
client.server_hardware.get.return_value = server_hardware
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power_state = power.OneViewPower().get_power_state(task)
|
||||
self.assertEqual(states.POWER_ON, power_state)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
def test_get_power_state_fail(self, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_hardware.get.side_effect = exc
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
power.OneViewPower().get_power_state,
|
||||
task)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_on(self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().set_power_state(task, states.POWER_ON)
|
||||
self.assertTrue(mock_set_boot_device.called)
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_called_once_with(power.POWER_ON, server_hardware,
|
||||
timeout=-1)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_on_with_timeout(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().set_power_state(task, states.POWER_ON,
|
||||
timeout=2)
|
||||
self.assertTrue(mock_set_boot_device.called)
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_called_once_with(power.POWER_ON, server_hardware,
|
||||
timeout=2)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_off(self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().set_power_state(task, states.POWER_OFF)
|
||||
self.assertFalse(mock_set_boot_device.called)
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_called_once_with(power.POWER_OFF, server_hardware,
|
||||
timeout=-1)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_off_with_timeout(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().set_power_state(task, states.POWER_OFF,
|
||||
timeout=2)
|
||||
self.assertFalse(mock_set_boot_device.called)
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_called_once_with(power.POWER_OFF, server_hardware,
|
||||
timeout=2)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_reboot(self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().set_power_state(task, states.REBOOT)
|
||||
calls = [mock.call(power.POWER_OFF, server_hardware, timeout=-1),
|
||||
mock.call(power.POWER_ON, server_hardware, timeout=-1)]
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_has_calls(calls)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_soft_reboot(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
interface = power.OneViewPower()
|
||||
interface.client = client
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
interface.set_power_state(task, states.SOFT_REBOOT)
|
||||
calls = [mock.call(power.SOFT_POWER_OFF, server_hardware,
|
||||
timeout=-1),
|
||||
mock.call(power.POWER_ON, server_hardware, timeout=-1)]
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_has_calls(calls)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_on_fail(self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_hardware.update_power_state.side_effect = exc
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
power.OneViewPower().set_power_state,
|
||||
task,
|
||||
states.POWER_ON)
|
||||
mock_set_boot_device.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_off_fail(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_hardware.update_power_state.side_effect = exc
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
power.OneViewPower().set_power_state,
|
||||
task,
|
||||
states.POWER_OFF)
|
||||
self.assertFalse(mock_set_boot_device.called)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_reboot_fail_with_hardware_on(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = {'powerState': 'On'}
|
||||
client.server_hardware.get.return_value = server_hardware
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_hardware.update_power_state.side_effect = exc
|
||||
interface = power.OneViewPower()
|
||||
interface.client = client
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
interface.reboot,
|
||||
task)
|
||||
self.assertFalse(mock_set_boot_device.called)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_reboot_fail_with_hardware_off(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = {'powerState': 'Off'}
|
||||
client.server_hardware.get.return_value = server_hardware
|
||||
exc = client_exception.HPOneViewException()
|
||||
client.server_hardware.update_power_state.side_effect = exc
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.OneViewError,
|
||||
power.OneViewPower().reboot,
|
||||
task)
|
||||
mock_set_boot_device.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_invalid_state(self, mock_set_boot_device):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(
|
||||
exception.InvalidParameterValue,
|
||||
power.OneViewPower().set_power_state,
|
||||
task,
|
||||
'fake_state')
|
||||
self.assertFalse(mock_set_boot_device.called)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_reboot_with_hardware_on(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = {'powerState': 'On'}
|
||||
client.server_hardware.get.return_value = server_hardware
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().reboot(task)
|
||||
calls = [mock.call(power.POWER_OFF, server_hardware, timeout=-1),
|
||||
mock.call(power.POWER_ON, server_hardware, timeout=-1)]
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_has_calls(calls)
|
||||
mock_set_boot_device.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_reboot_with_hardware_off(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = {'powerState': 'Off'}
|
||||
client.server_hardware.get.return_value = server_hardware
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().reboot(task, timeout=-1)
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_called_once_with(power.POWER_ON, server_hardware,
|
||||
timeout=-1)
|
||||
mock_set_boot_device.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(common, 'get_hponeview_client')
|
||||
@mock.patch.object(management, 'set_boot_device')
|
||||
def test_set_power_reboot_with_hardware_off_with_timeout(
|
||||
self, mock_set_boot_device, mock_get_ov_client):
|
||||
client = mock_get_ov_client()
|
||||
server_hardware = {'powerState': 'Off'}
|
||||
client.server_hardware.get.return_value = server_hardware
|
||||
server_hardware = self.node.driver_info.get('server_hardware_uri')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
power.OneViewPower().reboot(task, timeout=2)
|
||||
update = client.server_hardware.update_power_state
|
||||
update.assert_called_once_with(power.POWER_ON, server_hardware,
|
||||
timeout=2)
|
||||
mock_set_boot_device.assert_called_once_with(task)
|
||||
|
||||
def test_get_supported_power_states(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
supported_power_states = (
|
||||
task.driver.power.get_supported_power_states(task))
|
||||
self.assertEqual(set(power.SET_POWER_STATE_MAP),
|
||||
set(supported_power_states))
|
@ -1,126 +0,0 @@
|
||||
# Copyright (2015-2017) Hewlett Packard Enterprise Development LP
|
||||
# Copyright (2015-2017) Universidade Federal de Campina Grande
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Test class for HPE OneView Drivers."""
|
||||
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import noop
|
||||
from ironic.drivers.modules import pxe
|
||||
from ironic.drivers.modules.storage import noop as noop_storage
|
||||
from ironic.drivers import oneview
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
|
||||
class OneViewHardwareTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(OneViewHardwareTestCase, self).setUp()
|
||||
self.config(enabled_hardware_types=['oneview'],
|
||||
enabled_deploy_interfaces=[
|
||||
'oneview-direct', 'oneview-iscsi'],
|
||||
enabled_inspect_interfaces=['oneview'],
|
||||
enabled_management_interfaces=['oneview'],
|
||||
enabled_power_interfaces=['oneview'],
|
||||
enabled_raid_interfaces=['no-raid', 'agent'],
|
||||
enabled_console_interfaces=['no-console'],
|
||||
enabled_vendor_interfaces=['no-vendor'])
|
||||
|
||||
def test_default_interfaces(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='oneview')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self.assertIsInstance(task.driver.boot,
|
||||
pxe.PXEBoot)
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
oneview.deploy.OneViewIscsiDeploy)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
oneview.inspect.OneViewInspect)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
oneview.management.OneViewManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
oneview.power.OneViewPower),
|
||||
self.assertIsInstance(task.driver.storage,
|
||||
noop_storage.NoopStorage),
|
||||
self.assertIsInstance(task.driver.console,
|
||||
noop.NoConsole),
|
||||
self.assertIsInstance(task.driver.raid,
|
||||
noop.NoRAID)
|
||||
self.assertIsInstance(task.driver.vendor,
|
||||
noop.NoVendor)
|
||||
|
||||
def test_default_with_inspector_interface_enabled(self):
|
||||
self.config(enabled_inspect_interfaces=['inspector', 'oneview'])
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='oneview',
|
||||
deploy_interface='oneview-direct',
|
||||
inspect_interface='oneview',
|
||||
raid_interface='agent')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self.assertIsInstance(task.driver.boot,
|
||||
pxe.PXEBoot)
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
oneview.deploy.OneViewAgentDeploy)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
oneview.inspect.OneViewInspect)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
oneview.management.OneViewManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
oneview.power.OneViewPower)
|
||||
self.assertIsInstance(task.driver.raid,
|
||||
agent.AgentRAID)
|
||||
self.assertIsInstance(task.driver.vendor,
|
||||
noop.NoVendor)
|
||||
|
||||
def test_override_with_direct(self):
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='oneview',
|
||||
deploy_interface='oneview-direct',
|
||||
boot_interface='pxe',
|
||||
raid_interface='agent')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self.assertIsInstance(task.driver.boot,
|
||||
pxe.PXEBoot)
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
oneview.deploy.OneViewAgentDeploy)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
oneview.inspect.OneViewInspect)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
oneview.management.OneViewManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
oneview.power.OneViewPower)
|
||||
self.assertIsInstance(task.driver.raid,
|
||||
agent.AgentRAID)
|
||||
|
||||
def test_override_with_iscsi(self):
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='oneview',
|
||||
deploy_interface='oneview-iscsi',
|
||||
boot_interface='pxe',
|
||||
raid_interface='agent')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self.assertIsInstance(task.driver.boot,
|
||||
pxe.PXEBoot)
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
oneview.deploy.OneViewIscsiDeploy)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
oneview.inspect.OneViewInspect)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
oneview.management.OneViewManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
oneview.power.OneViewPower)
|
||||
self.assertIsInstance(task.driver.raid,
|
||||
agent.AgentRAID)
|
@ -123,24 +123,6 @@ SCCICLIENT_VIOM_CONF_SPEC = (
|
||||
'terminate',
|
||||
)
|
||||
|
||||
HPE_ONEVIEW_SPEC = (
|
||||
'oneview_client',
|
||||
'resources',
|
||||
'exceptions',
|
||||
)
|
||||
|
||||
HPE_ONEVIEW_CLS_SPEC = (
|
||||
)
|
||||
|
||||
HPE_ONEVIEW_STATES_SPEC = (
|
||||
'ONEVIEW_POWER_OFF',
|
||||
'ONEVIEW_POWERING_OFF',
|
||||
'ONEVIEW_POWER_ON',
|
||||
'ONEVIEW_POWERING_ON',
|
||||
'ONEVIEW_RESETTING',
|
||||
'ONEVIEW_ERROR',
|
||||
)
|
||||
|
||||
REDFISH_SPEC = (
|
||||
'redfish',
|
||||
)
|
||||
|
@ -25,7 +25,6 @@ Current list of mocked libraries:
|
||||
- proliantutils
|
||||
- pysnmp
|
||||
- scciclient
|
||||
- hpOneView
|
||||
- pywsman
|
||||
- python-dracclient
|
||||
"""
|
||||
@ -69,30 +68,13 @@ if not proliantutils:
|
||||
if 'ironic.drivers.ilo' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.ilo'])
|
||||
|
||||
|
||||
hpOneView = importutils.try_import('hpOneView')
|
||||
if not hpOneView:
|
||||
hpOneView = mock.MagicMock(spec_set=mock_specs.HPE_ONEVIEW_SPEC)
|
||||
sys.modules['hpOneView'] = hpOneView
|
||||
sys.modules['hpOneView.oneview_client'] = hpOneView.oneview_client
|
||||
sys.modules['hpOneView.resources'] = hpOneView.resources
|
||||
sys.modules['hpOneView.exceptions'] = hpOneView.exceptions
|
||||
hpOneView.exceptions.HPOneViewException = type('HPOneViewException',
|
||||
(Exception,), {})
|
||||
sys.modules['hpOneView.oneview_client'].OneViewClient = mock.MagicMock(
|
||||
spec_set=mock_specs.HPE_ONEVIEW_CLS_SPEC
|
||||
)
|
||||
if 'ironic.drivers.oneview' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview'])
|
||||
|
||||
|
||||
redfish = importutils.try_import('redfish')
|
||||
if not redfish:
|
||||
redfish = mock.MagicMock(spec_set=mock_specs.REDFISH_SPEC)
|
||||
sys.modules['redfish'] = redfish
|
||||
if 'ironic.drivers.oneview' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview'])
|
||||
|
||||
if 'ironic.drivers.redfish' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.redfish'])
|
||||
|
||||
# attempt to load the external 'python-dracclient' library, which is required
|
||||
# by the optional drivers.modules.drac module
|
||||
|
7
releasenotes/notes/remove-oneview-9315c7b926fd4aa2.yaml
Normal file
7
releasenotes/notes/remove-oneview-9315c7b926fd4aa2.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
other:
|
||||
- |
|
||||
The ``oneview`` hardware type and related interfaces have been
|
||||
removed due to a lack of maintainer and 3rd-party CI. Please see
|
||||
`story 2001924 <https://storyboard.openstack.org/#!/story/2001924>`_
|
||||
for additional information.
|
@ -79,8 +79,6 @@ ironic.hardware.interfaces.deploy =
|
||||
direct = ironic.drivers.modules.agent:AgentDeploy
|
||||
fake = ironic.drivers.modules.fake:FakeDeploy
|
||||
iscsi = ironic.drivers.modules.iscsi_deploy:ISCSIDeploy
|
||||
oneview-direct = ironic.drivers.modules.oneview.deploy:OneViewAgentDeploy
|
||||
oneview-iscsi = ironic.drivers.modules.oneview.deploy:OneViewIscsiDeploy
|
||||
ramdisk = ironic.drivers.modules.pxe:PXERamdiskDeploy
|
||||
|
||||
ironic.hardware.interfaces.inspect =
|
||||
@ -90,7 +88,6 @@ ironic.hardware.interfaces.inspect =
|
||||
inspector = ironic.drivers.modules.inspector:Inspector
|
||||
irmc = ironic.drivers.modules.irmc.inspect:IRMCInspect
|
||||
no-inspect = ironic.drivers.modules.noop:NoInspect
|
||||
oneview = ironic.drivers.modules.oneview.inspect:OneViewInspect
|
||||
|
||||
ironic.hardware.interfaces.management =
|
||||
cimc = ironic.drivers.modules.cimc.management:CIMCManagement
|
||||
@ -100,7 +97,6 @@ ironic.hardware.interfaces.management =
|
||||
ipmitool = ironic.drivers.modules.ipmitool:IPMIManagement
|
||||
irmc = ironic.drivers.modules.irmc.management:IRMCManagement
|
||||
noop = ironic.drivers.modules.noop_mgmt:NoopManagement
|
||||
oneview = ironic.drivers.modules.oneview.management:OneViewManagement
|
||||
redfish = ironic.drivers.modules.redfish.management:RedfishManagement
|
||||
ucsm = ironic.drivers.modules.ucs.management:UcsManagement
|
||||
xclarity = ironic.drivers.modules.xclarity.management:XClarityManagement
|
||||
@ -117,7 +113,6 @@ ironic.hardware.interfaces.power =
|
||||
ilo = ironic.drivers.modules.ilo.power:IloPower
|
||||
ipmitool = ironic.drivers.modules.ipmitool:IPMIPower
|
||||
irmc = ironic.drivers.modules.irmc.power:IRMCPower
|
||||
oneview = ironic.drivers.modules.oneview.power:OneViewPower
|
||||
redfish = ironic.drivers.modules.redfish.power:RedfishPower
|
||||
snmp = ironic.drivers.modules.snmp:SNMPPower
|
||||
ucsm = ironic.drivers.modules.ucs.power:Power
|
||||
@ -157,7 +152,6 @@ ironic.hardware.types =
|
||||
ipmi = ironic.drivers.ipmi:IPMIHardware
|
||||
irmc = ironic.drivers.irmc:IRMCHardware
|
||||
manual-management = ironic.drivers.generic:ManualManagementHardware
|
||||
oneview = ironic.drivers.oneview:OneViewHardware
|
||||
redfish = ironic.drivers.redfish:RedfishHardware
|
||||
snmp = ironic.drivers.snmp:SNMPHardware
|
||||
xclarity = ironic.drivers.xclarity:XClarityHardware
|
||||
|
Loading…
Reference in New Issue
Block a user