First implementation of the new rsd-virt-for-nova driver
- Includes driver code - Some unit tests - Some documentation - Devstack installation plugin Change-Id: Ie3b2483fbf587a037a3fd9d6af1bd64fa5635ed2 Signed-off-by: Helena McGough <helena.mcgough@intel.com>
This commit is contained in:
parent
20e0545b5d
commit
bdc51defdc
@ -15,8 +15,8 @@
|
||||
README
|
||||
######
|
||||
|
||||
nova-rsd
|
||||
--------
|
||||
rsd-virt-for-nova
|
||||
-----------------
|
||||
|
||||
A nova virt driver to communicate with IntelRSD's PODM and PSME to create
|
||||
composed nodes.
|
||||
|
3
devstack/override-defaults
Normal file
3
devstack/override-defaults
Normal file
@ -0,0 +1,3 @@
|
||||
# Plug-in overrides
|
||||
|
||||
VIRT_DRIVER=rsd
|
74
devstack/plugin.sh
Normal file
74
devstack/plugin.sh
Normal file
@ -0,0 +1,74 @@
|
||||
local xtrace=$(set +o | grep xtrace)
|
||||
local error_on_clone=${ERROR_ON_CLONE}
|
||||
if [ "$VERBOSE" == 'True' ]; then
|
||||
# enabling verbosity on whole plugin - default behavior
|
||||
set -o xtrace
|
||||
fi
|
||||
|
||||
function configure_nova_rsd {
|
||||
# set neutron configs
|
||||
iniset $NEUTRON_CONF quotas quota_network -1
|
||||
iniset $NEUTRON_CONF quotas quota_subnet -1
|
||||
iniset $NEUTRON_CONF quotas quota_port -1
|
||||
iniset $NEUTRON_CONF quotas quota_security_group -1
|
||||
iniset $NEUTRON_CONF quotas quota_security_group_rule -1
|
||||
|
||||
# set nova configs
|
||||
iniset $NOVA_CONF DEFAULT compute_driver "rsd.driver.RSDDriver"
|
||||
iniset $NOVA_CONF DEFAULT cpu_allocation_ratio 1.0
|
||||
iniset $NOVA_CONF DEFAULT ram_allocation_ratio 1.0
|
||||
# Disable arbitrary limits
|
||||
iniset $NOVA_CONF DEFAULT quota_instances -1
|
||||
iniset $NOVA_CONF DEFAULT quota_cores -1
|
||||
iniset $NOVA_CONF DEFAULT quota_ram -1
|
||||
iniset $NOVA_CONF DEFAULT quota_floating_ips -1
|
||||
iniset $NOVA_CONF DEFAULT quota_fixed_ips -1
|
||||
iniset $NOVA_CONF DEFAULT quota_metadata_items -1
|
||||
iniset $NOVA_CONF DEFAULT quota_injected_files -1
|
||||
iniset $NOVA_CONF DEFAULT quota_injected_file_path_length -1
|
||||
iniset $NOVA_CONF DEFAULT quota_security_groups -1
|
||||
iniset $NOVA_CONF DEFAULT quota_security_group_rules -1
|
||||
iniset $NOVA_CONF DEFAULT quota_key_pairs -1
|
||||
iniset $NOVA_CONF filter_scheduler enabled_filters "RetryFilter,AvailabilityZoneFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,CoreFilter,RamFilter,DiskFilter"
|
||||
|
||||
iniset $NOVA_CONF rsd podm_ip ${PODM_IP}
|
||||
iniset $NOVA_CONF rsd podm_user ${PODM_USER}
|
||||
iniset $NOVA_CONF rsd podm_password ${PODM_PASSWD}
|
||||
iniset $NOVA_CONF rsd podm_port ${PODM_PORT}
|
||||
}
|
||||
|
||||
# disabling ERROR_NO_CLONE to allow this plugin work with devstack-gate
|
||||
ERROR_ON_CLONE=False
|
||||
|
||||
case $1 in
|
||||
"stack")
|
||||
case $2 in
|
||||
"pre-install")
|
||||
# cloning source code
|
||||
echo_summary "Cloning of src files for nova-rsd not required"
|
||||
# sudo pip install -e "git+https://github.com/openstack/rsd-lib@517275b24fc86ce67a345b3aae2d4fa8564d18c1#egg=rsd_lib"
|
||||
;;
|
||||
"install")
|
||||
sudo pip install -e "${NOVA_RSD_DIR}"
|
||||
;;
|
||||
"post-config")
|
||||
configure_nova_rsd
|
||||
;;
|
||||
"extra")
|
||||
:
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
"unstack")
|
||||
sudo pip uninstall "${NOVA_RSD_DIR}"
|
||||
;;
|
||||
"clean")
|
||||
# Remove state and transient data
|
||||
# Remember clean.sh first calls unstack.sh
|
||||
# this is a noop
|
||||
:
|
||||
;;
|
||||
esac
|
||||
|
||||
ERROR_ON_CLONE=$error_on_clone
|
||||
$xtrace
|
31
devstack/settings
Normal file
31
devstack/settings
Normal file
@ -0,0 +1,31 @@
|
||||
# This driver is enabled in override-defaults with:
|
||||
# VIRT_DRIVER=${VIRT_DRIVER:-rsd}
|
||||
|
||||
if [ "$VERBOSE" == "False" ]; then
|
||||
# allow local debugging
|
||||
set -o xtrace
|
||||
fi
|
||||
|
||||
NOVA_RSD_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )
|
||||
NOVA_RSD_REPO_ENABLE=$(trueorfalse True NOVA_RSD_REPO_ENABLE)
|
||||
|
||||
if [ "${NOVA_RSD_REPO_ENABLE}" == "True" ]; then
|
||||
# Ensure that the fake neutron agent is enabled
|
||||
if [[ ! "$Q_ML2_PLUGIN_MECHANISM_DRIVERS" =~ "openvswitch" ]]; then
|
||||
Q_ML2_PLUGIN_MECHANISM_DRIVERS+=",openvswitch"
|
||||
fi
|
||||
if [[ ! "$Q_ML2_PLUGIN_MECHANISM_DRIVERS" =~ "fake_agent" ]]; then
|
||||
Q_ML2_PLUGIN_MECHANISM_DRIVERS+=",fake_agent"
|
||||
fi
|
||||
fi
|
||||
|
||||
# RSD comfigurations
|
||||
PODM_IP=${PODM_IP:-'localhost'}
|
||||
PODM_USER=${PODM_USER:-'admin'}
|
||||
PODM_PASSWD=${PODM_PASSWD:-'admin'}
|
||||
PODM_PORT=${PODM_PORT:-8443}
|
||||
|
||||
if [ "$VERBOSE" == "False" ]; then
|
||||
# turn off debugging again
|
||||
set +o xtrace
|
||||
fi
|
160
doc/source/conf.py
Normal file
160
doc/source/conf.py
Normal file
@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
"""Configuration for documentation setup."""
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = u'rsd_virt_for_nova'
|
||||
copyright = u'2018, Intel Corporation'
|
||||
author = u'Helena McGough'
|
||||
|
||||
# The short X.Y version
|
||||
version = u''
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = u''
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path .
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# The default sidebars (for documents that don't match any pattern) are
|
||||
# defined by theme itself. Builtin themes are using these templates by
|
||||
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
||||
# 'searchbox.html']``.
|
||||
#
|
||||
# html_sidebars = {}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'rsd_virt_for_novadoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'rsd_virt_for_nova.tex', u'nova\\_rsd Documentation',
|
||||
u'Helena McGough', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'rsd_virt_for_nova', u'rsd_virt_for_nova Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'rsd_virt_for_nova', u'rsd_virt_for_nova Documentation'
|
||||
author, 'rsd_virt_for_nova', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# -- Extension configuration -------------------------------------------------
|
29
doc/source/examples/sample_local.conf
Normal file
29
doc/source/examples/sample_local.conf
Normal file
@ -0,0 +1,29 @@
|
||||
[[local|localrc]]
|
||||
|
||||
HOST_IP=<HOST_IP_ADDRESS>
|
||||
|
||||
SERVICE_TOKEN=password
|
||||
ADMIN_PASSWORD=password
|
||||
MYSQL_PASSWORD=password
|
||||
RABBIT_PASSWORD=password
|
||||
DATABASE_PASSWORD=password
|
||||
SERVICE_PASSWORD=$ADMIN_PASSWORD
|
||||
|
||||
LOGFILE=$DEST/logs/stack.sh.log
|
||||
LOGDAYS=2
|
||||
|
||||
GIT_BASE=https://github.com
|
||||
RECLONE=True
|
||||
|
||||
SUBNETPOOL_PREFIX_V4=${SUBNETPOOL_PREFIX_V4:-192.168.208.0/20}
|
||||
SUBNETPOOL_SIZE_V4=${SUBNETPOOL_SIZE_V4:-25}
|
||||
|
||||
enable_plugin rsd-virt-for-nova <PATH_TO_NOVA_RSD_REPO> <REPO_BRANCH_NAME>
|
||||
NOVA_RSD_REPO_ENABLE=True
|
||||
|
||||
# Optional RSD deployment parameters can be specified
|
||||
# Defaults are defined below but can be changed once configured
|
||||
# PODM_IP=localhost
|
||||
# PODM_USER=admin
|
||||
# PODM_PASSWD=admin
|
||||
# PODM_PORT=8443
|
34
doc/source/index.rst
Normal file
34
doc/source/index.rst
Normal file
@ -0,0 +1,34 @@
|
||||
..
|
||||
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.
|
||||
|
||||
Welcome to nova_rsd's documentation!
|
||||
====================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents
|
||||
|
||||
* :ref:`examples/sample_local.conf`
|
||||
* :ref:`installation_guide.rst`
|
||||
* :ref:`known_issues.rst`
|
||||
* :ref:`user_guide.rsd`
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
102
doc/source/installation_guide.rst
Normal file
102
doc/source/installation_guide.rst
Normal file
@ -0,0 +1,102 @@
|
||||
..
|
||||
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.
|
||||
|
||||
==================
|
||||
Installation Guide
|
||||
==================
|
||||
|
||||
Pre-requisites
|
||||
--------------
|
||||
|
||||
* Access to the internet
|
||||
* An IntelRSD deployment, managed by a PODM
|
||||
* Min. one PSME communicating with yout PODM, with available compute systems
|
||||
|
||||
|
||||
Installation of OpenStack
|
||||
-------------------------
|
||||
|
||||
The following guide provides developer instructions on how to deploy OpenStack
|
||||
using the deployment tool DevStack:
|
||||
|
||||
https://docs.openstack.org/devstack/latest/guides/single-machine.html
|
||||
|
||||
Once you have set up your environment to deploy DevStack please refer to the
|
||||
below configuration instructions to enable the nova-rsd driver contained in
|
||||
this repo.
|
||||
|
||||
DevStack Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These configuration instructions provide directions for this minimal
|
||||
installation of the nova-rsd driver with OpenStack. It also assumes that you
|
||||
already have setup and configured an RSD PODM that is communicating with
|
||||
available composable compute systems, via one or more PSMEs.
|
||||
|
||||
.. Note::
|
||||
|
||||
For instructions on setting up a PODM and PSMEs please refer to the user guides
|
||||
and code repositories referred to in the following links:
|
||||
|
||||
https://github.com/intel/intelRSD
|
||||
|
||||
https://www.intel.com/content/www/us/en/architecture-and-technology/rack-scale-design/rack-scale-design-resources.html
|
||||
|
||||
|
||||
A sample local.conf for DevStack is provided here, `examples/sample_local.conf`.
|
||||
Please copy this file into your devstack reposititory and rename it local.conf
|
||||
and adjust the configuration options provided where appropriate.
|
||||
|
||||
::
|
||||
|
||||
cp examples/sample_local.conf devstack/local.conf
|
||||
|
||||
local.conf settings
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the local.conf the following parameters can be changed:
|
||||
|
||||
* HOST_IP:
|
||||
Set this option to be the ip address of where your OpenStack deployment
|
||||
will be running.
|
||||
|
||||
* ``enable_plugin nova-rsd <PATH_TO_NOVA_RSD_REPO> <REPO_BRANCH_NAME>``:
|
||||
Clone the nova-rsd repository and point PATH_TO_NOVA_RSD_REPO to its
|
||||
location.
|
||||
|
||||
Set the REPO_BRANCH_NAME to be the branch of the above repo to be the one
|
||||
with the code version that you require. If left unspecified will default to
|
||||
master.
|
||||
|
||||
* PODM_IP:
|
||||
This parameter specifies the IP address of where your PODM is running to
|
||||
facilitate communication with its APIs.
|
||||
|
||||
Default: ``localhost``
|
||||
|
||||
* PODM_USER:
|
||||
This parameter specifies the username to authenticate to the PODM with.
|
||||
|
||||
Default: ``admin``
|
||||
|
||||
* PODM_PASSWD:
|
||||
This parameter specifies the password to authenticate to the PODM with,
|
||||
inconjuction with the username defined above.
|
||||
|
||||
Default: ``admin``
|
||||
|
||||
* PODM_PORT:
|
||||
This parameter specifies the port that the PODM is certified to transport
|
||||
trsffic through.
|
||||
|
||||
Default: ``8443``
|
40
doc/source/known_issues.rst
Normal file
40
doc/source/known_issues.rst
Normal file
@ -0,0 +1,40 @@
|
||||
..
|
||||
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.
|
||||
|
||||
============
|
||||
Known Issues
|
||||
============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This project is being created as a new out-of-tree virt driver for nova. It is
|
||||
currently a work-in-progress and there are a few issues and area that need to
|
||||
be looked into and developed to bring the project to maturity.
|
||||
|
||||
|
||||
Issues + Future work
|
||||
--------------------
|
||||
|
||||
* Implementation of additional unit tests and inclusion of functional tests for
|
||||
the complete code coverage of the new virt driver.
|
||||
|
||||
* Needs to be tested on physical RSD compatible hardware.
|
||||
|
||||
* Implement the usage of the EventSubscription service provided by the PODM to
|
||||
improve the tracking of the removal/addition of physical resources from the
|
||||
RSD deployment.
|
||||
|
||||
* Check the status of the creation of duplicate flavors when they are auto
|
||||
generated. May have to be re-implemented now that the extra_specs are being
|
||||
used by the resource provider to track the inventory of the systems.
|
98
doc/source/user_guide.rst
Normal file
98
doc/source/user_guide.rst
Normal file
@ -0,0 +1,98 @@
|
||||
..
|
||||
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.
|
||||
|
||||
==========
|
||||
User guide
|
||||
==========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This project is a wip. It is a new nova-virt driver that allows the management
|
||||
an IntelRSD deployment software architecture. This architecture deployment
|
||||
facilitates the orchestration of a composable infrastructure through OpenStack.
|
||||
The new driver itself enables the management of RSD composable nodes through
|
||||
the use of nova compute service. Therefore you can manage the deployement of a
|
||||
composed node like you would a VM, container or a baremetal instance, etc.
|
||||
|
||||
To set up your OpenStack deployment to use the new virt driver follow the
|
||||
instructions provided in `installation_guide.rst`.
|
||||
|
||||
|
||||
Usage Instructions
|
||||
------------------
|
||||
|
||||
The new nova-rsd virt driver aims to use the same CLI commands as any of the
|
||||
other standard virt drivers.
|
||||
|
||||
Currently the following commands are supported by the new nova-rsd virt driver:
|
||||
|
||||
::
|
||||
|
||||
openstack server create --image <IMAGE_ID> --flavor <FLAVOR_ID> <INSTANCE_NAME>
|
||||
|
||||
openstack server delete <INSTANCE_NAME>
|
||||
|
||||
openstack server start <INSTANCE_NAME>
|
||||
|
||||
openstack server stop <INSTANCE_NAME>
|
||||
|
||||
openstack server reboot --hard <INSTANCE_NAME>
|
||||
|
||||
openstack server reboot --soft <INSTANCE_NAME>
|
||||
|
||||
openstack server reboot <INSTANCE_NAME>
|
||||
|
||||
|
||||
To create an instance of type composed node through OpenStack you have to use
|
||||
one of the specific RSD flavors that are automatically generated based on the
|
||||
resources available in the RSD deployment. These flavors can be identified by
|
||||
their name and the extra_specs used to define them when created. The
|
||||
extra_specs define the custom resources used by the resource provider to track
|
||||
resource consumption through the placement API.
|
||||
|
||||
.. Example::
|
||||
|
||||
+----------------------------+----------------------------+
|
||||
| Field | Value |
|
||||
+----------------------------+----------------------------+
|
||||
| OS-FLV-DISABLED:disabled | False |
|
||||
| OS-FLV-EXT-DATA:ephemeral | 0 |
|
||||
| access_project_ids | None |
|
||||
| disk | 0 |
|
||||
| id | 785919MB-32vcpus |
|
||||
| name | RSD-785919MB-32vcpus |
|
||||
| os-flavor-access:is_public | True |
|
||||
| properties | resources:CUSTOM_1_S_3='1' |
|
||||
| ram | 785919 |
|
||||
| rxtx_factor | 1.0 |
|
||||
| swap | |
|
||||
| vcpus | 32 |
|
||||
+----------------------------+----------------------------+
|
||||
|
||||
|
||||
In general a flavor can only be used once depending on the amount of composable
|
||||
systems available in the RSD deployment. They will define the specific system that
|
||||
will be consumed from the RSD deployment.
|
||||
|
||||
|
||||
Usage Tracking
|
||||
--------------
|
||||
|
||||
The consumption of resource can be tracked through nova hypervisors used to boot
|
||||
the composed node instances from. These are defined at the ``Chassis`` level of
|
||||
the RSD deployment.
|
||||
It can also be tracked in the placement API in terms of resource providers and
|
||||
their inventory. There is a resource provider for each of the hypervisors at
|
||||
the ``Chassis`` level and the a child resource provider for each ``System``
|
||||
contained within the defined ``Chassis``.
|
15
nova/__init__.py
Normal file
15
nova/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
"""Initialize virt driver."""
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
15
nova/virt/__init__.py
Normal file
15
nova/virt/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
"""Initialize virt driver."""
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
1
nova/virt/rsd/__init__.py
Normal file
1
nova/virt/rsd/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Initialize new virt driver."""
|
18
nova/virt/rsd/driver.py
Normal file
18
nova/virt/rsd/driver.py
Normal file
@ -0,0 +1,18 @@
|
||||
# 2017 - 2018 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Initialize new virt driver for nova."""
|
||||
|
||||
import rsd_virt_for_nova.virt.rsd.driver as rsd_driver
|
||||
|
||||
RSDDriver = rsd_driver.RSDDriver
|
22
rsd_virt_for_nova/conf/__init__.py
Normal file
22
rsd_virt_for_nova/conf/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
"""Register new config options for RSD."""
|
||||
|
||||
import nova.conf
|
||||
|
||||
from rsd_virt_for_nova.conf import rsd
|
||||
|
||||
CONF = nova.conf.CONF
|
||||
|
||||
rsd.register_opts(CONF)
|
51
rsd_virt_for_nova/conf/rsd.py
Normal file
51
rsd_virt_for_nova/conf/rsd.py
Normal file
@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
"""Additional configuration opentions for the rsd-virt driver."""
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
rsd_group = cfg.OptGroup(
|
||||
'rsd',
|
||||
title='RSD Options')
|
||||
|
||||
|
||||
rsd_opts = [
|
||||
cfg.StrOpt('podm_ip',
|
||||
default='localhost',
|
||||
help='Specifying the IP address of the PODM which is talking '
|
||||
'to the appropriate PSME. Defaults to localhost, '
|
||||
'assuming PODM is running on the same machine as '
|
||||
'OpenStack. '),
|
||||
cfg.StrOpt('podm_user',
|
||||
default='admin',
|
||||
help='Specifying the username for communication with the '
|
||||
'PODM. '),
|
||||
cfg.StrOpt('podm_password',
|
||||
default='admin',
|
||||
help='Specifying the password for communication with the '
|
||||
'PODM. '),
|
||||
cfg.IntOpt('podm_port',
|
||||
default=8443,
|
||||
help='Specifying port on PODM for communication. ')
|
||||
]
|
||||
|
||||
STATIC_OPTIONS = (rsd_opts)
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
"""Register the new configuration options for RSD."""
|
||||
conf.register_group(rsd_group)
|
||||
conf.register_opts(STATIC_OPTIONS, group=rsd_group)
|
1
rsd_virt_for_nova/tests/__init__.py
Normal file
1
rsd_virt_for_nova/tests/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Initialize tests for virt driver."""
|
1
rsd_virt_for_nova/tests/conf/__init__.py
Normal file
1
rsd_virt_for_nova/tests/conf/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Initialize config for the virt driver."""
|
42
rsd_virt_for_nova/tests/conf/test_conf.py
Normal file
42
rsd_virt_for_nova/tests/conf/test_conf.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 2017 - 2018 Intel Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Tests for the RSD configuration options."""
|
||||
|
||||
from nova import test
|
||||
|
||||
from rsd_virt_for_nova import conf as cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestConf(test.NoDBTestCase):
|
||||
"""Test class for configurations."""
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize configuration test class."""
|
||||
super(TestConf, self).setUp()
|
||||
|
||||
def test_conf(self):
|
||||
"""Test the default rsd config values."""
|
||||
# Try an option from each grouping of static options
|
||||
|
||||
# PODM IP
|
||||
self.assertEqual('localhost', CONF.rsd.podm_ip)
|
||||
# PODM username
|
||||
self.assertEqual('admin', CONF.rsd.podm_user)
|
||||
# PODM password
|
||||
self.assertEqual('admin', CONF.rsd.podm_password)
|
||||
# PODM port
|
||||
self.assertEqual(8443, CONF.rsd.podm_port)
|
10
rsd_virt_for_nova/tests/json_samples/chas_inv_col.json
Normal file
10
rsd_virt_for_nova/tests/json_samples/chas_inv_col.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Chassis",
|
||||
"@odata.id": "/redfish/v1/Chassis",
|
||||
"@odata.type": "#ChassisCollection.ChassisCollection",
|
||||
"Members": [
|
||||
],
|
||||
"Members@odata.count": 8,
|
||||
"Name": "Chassis Collection"
|
||||
|
||||
}
|
73
rsd_virt_for_nova/tests/json_samples/chassis.json
Normal file
73
rsd_virt_for_nova/tests/json_samples/chassis.json
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Chassis/Members/$entity",
|
||||
"@odata.id": "/redfish/v1/Chassis/Chassis1",
|
||||
"@odata.type": "#Chassis.1.0.0.Chassis",
|
||||
"AssetTag": "FlexChassis1",
|
||||
"ChassisType": "Drawer",
|
||||
"Description": "this is a chassis",
|
||||
"Id": "Chassis1",
|
||||
"IndicatorLED": "On",
|
||||
"Links": {
|
||||
"ComputerSystems": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System1"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System2"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System3"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System4"
|
||||
}
|
||||
],
|
||||
"ContainedBy": {
|
||||
"@odata.id": "/redfish/v1/Chassis/Rack1"
|
||||
},
|
||||
"Contains": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Chassis1"
|
||||
}
|
||||
],
|
||||
"ManagedBy": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Managers/manager1"
|
||||
}
|
||||
],
|
||||
"ManagersIn": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Managers/manager1"
|
||||
}
|
||||
],
|
||||
"Oem": { },
|
||||
"Switches": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/EthernetSwitches/switch1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Manufacturer": "Intel Corporaion",
|
||||
"Model": "Lenovo FLEX 8731",
|
||||
"Name": "FLEX-1",
|
||||
"Oem": {
|
||||
"Intel:RackScale": {
|
||||
"@odata.type": "#Intel.Oem.Chassis",
|
||||
"Location": {
|
||||
"Rack": "Rack1",
|
||||
"UHeight": "10 U",
|
||||
"ULocation": "25 U",
|
||||
"UWidth": "1 U"
|
||||
},
|
||||
"UUID": "e1c2d764-5c72-36d6-9945-a78255edab51"
|
||||
}
|
||||
},
|
||||
"PartNumber": "5c72-36d6",
|
||||
"SKU": "e1c2d764-5c72",
|
||||
"SerialNumber": "a78255edab51",
|
||||
"Status": {
|
||||
"Health": "OK",
|
||||
"HealthRollup": "OK",
|
||||
"State": "Enabled"
|
||||
}
|
||||
}
|
37
rsd_virt_for_nova/tests/json_samples/chassis_col.json
Normal file
37
rsd_virt_for_nova/tests/json_samples/chassis_col.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Chassis",
|
||||
"@odata.id": "/redfish/v1/Chassis",
|
||||
"@odata.type": "#ChassisCollection.ChassisCollection",
|
||||
"Members": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Rack1"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Rack2"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Chassis1"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Chassis2"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Drawer1"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Drawer2"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/NVMEChassis1"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/NVMEChassis2"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/System1"
|
||||
}
|
||||
],
|
||||
"Members@odata.count": 8,
|
||||
"Name": "Chassis Collection"
|
||||
|
||||
}
|
114
rsd_virt_for_nova/tests/json_samples/node.json
Normal file
114
rsd_virt_for_nova/tests/json_samples/node.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Nodes/Members/$entity",
|
||||
"@odata.id": "/redfish/v1/Nodes/Node1",
|
||||
"@odata.type": "#ComposedNode.1.1.0.ComposedNode",
|
||||
"Id": "Node1",
|
||||
"Name": "Composed Node",
|
||||
"Description": "Node #1",
|
||||
"UUID": "fa39d108-7d70-400a-9db2-6940375c31c2",
|
||||
"PowerState": "Off",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup": "OK"
|
||||
},
|
||||
"Processors": {
|
||||
"Count": 2,
|
||||
"Model": "Multi-Core Intel(R) Xeon(R) processor 7xxx Series",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup" : "OK"
|
||||
}
|
||||
},
|
||||
"Memory": {
|
||||
"TotalSystemMemoryGiB": 32,
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup" : "OK"
|
||||
}
|
||||
},
|
||||
"ComposedNodeState": "Allocated",
|
||||
"Boot": {
|
||||
"BootSourceOverrideEnabled": "Disabled",
|
||||
"BootSourceOverrideTarget": "None",
|
||||
"BootSourceOverrideTarget@Redfish.AllowableValues": [
|
||||
"None",
|
||||
"Pxe",
|
||||
"Hdd",
|
||||
"RemoteDrive"
|
||||
],
|
||||
"BootSourceOverrideMode": "Legacy",
|
||||
"BootSourceOverrideMode@Redfish.AllowableValues": ["Legacy",
|
||||
"UEFI"]
|
||||
},
|
||||
"Oem": {},
|
||||
"Links": {
|
||||
"ComputerSystem": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1"
|
||||
},
|
||||
"Processors": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Processors/CPU1"
|
||||
}
|
||||
],
|
||||
"Memory": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Memory/Dimm1"
|
||||
}
|
||||
],
|
||||
"EthernetInterfaces": [
|
||||
{
|
||||
"@odata.id":
|
||||
"/redfish/v1/Systems/System1/EthernetInterfaces/LAN1"
|
||||
}
|
||||
],
|
||||
"LocalDrives": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Blade1/Drives/1"
|
||||
}
|
||||
],
|
||||
"RemoteDrives": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Services/RSS1/Targets/target1"
|
||||
}
|
||||
],
|
||||
"ManagedBy": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Managers/PODM"
|
||||
}
|
||||
],
|
||||
"Oem": {}
|
||||
},
|
||||
"Actions": {
|
||||
"#ComposedNode.Reset": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.Reset",
|
||||
"ResetType@Redfish.AllowableValues": [
|
||||
"On",
|
||||
"ForceOff",
|
||||
"GracefulRestart",
|
||||
"ForceRestart",
|
||||
"Nmi",
|
||||
"ForceOn",
|
||||
"PushPowerButton",
|
||||
"GracefulShutdown"
|
||||
]
|
||||
},
|
||||
"#ComposedNode.Assemble": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.Assemble"
|
||||
},
|
||||
"#ComposedNode.AttachResource": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.AttachResource",
|
||||
"@Redfish.ActionInfo": {
|
||||
"@odata.id":"/redfish/v1/Nodes/Node1/Actions/AttachResourceActionInfo"
|
||||
}
|
||||
},
|
||||
"#ComposedNode.DetachResource": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.DetachResource",
|
||||
"@Redfish.ActionInfo": {
|
||||
"@odata.id":"/redfish/v1/Nodes/Node1/Actions/DetachResourceActionInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
rsd_virt_for_nova/tests/json_samples/node_assembled.json
Normal file
114
rsd_virt_for_nova/tests/json_samples/node_assembled.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Nodes/Members/$entity",
|
||||
"@odata.id": "/redfish/v1/Nodes/Node1",
|
||||
"@odata.type": "#ComposedNode.1.1.0.ComposedNode",
|
||||
"Id": "Node1",
|
||||
"Name": "Composed Node",
|
||||
"Description": "Node #1",
|
||||
"UUID": "fa39d108-7d70-400a-9db2-6940375c31c2",
|
||||
"PowerState": "Off",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup": "OK"
|
||||
},
|
||||
"Processors": {
|
||||
"Count": 2,
|
||||
"Model": "Multi-Core Intel(R) Xeon(R) processor 7xxx Series",
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup" : "OK"
|
||||
}
|
||||
},
|
||||
"Memory": {
|
||||
"TotalSystemMemoryGiB": 32,
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup" : "OK"
|
||||
}
|
||||
},
|
||||
"ComposedNodeState": "Assembled",
|
||||
"Boot": {
|
||||
"BootSourceOverrideEnabled": "Disabled",
|
||||
"BootSourceOverrideTarget": "None",
|
||||
"BootSourceOverrideTarget@Redfish.AllowableValues": [
|
||||
"None",
|
||||
"Pxe",
|
||||
"Hdd",
|
||||
"RemoteDrive"
|
||||
],
|
||||
"BootSourceOverrideMode": "Legacy",
|
||||
"BootSourceOverrideMode@Redfish.AllowableValues": ["Legacy",
|
||||
"UEFI"]
|
||||
},
|
||||
"Oem": {},
|
||||
"Links": {
|
||||
"ComputerSystem": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1"
|
||||
},
|
||||
"Processors": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Processors/CPU1"
|
||||
}
|
||||
],
|
||||
"Memory": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Memory/Dimm1"
|
||||
}
|
||||
],
|
||||
"EthernetInterfaces": [
|
||||
{
|
||||
"@odata.id":
|
||||
"/redfish/v1/Systems/System1/EthernetInterfaces/LAN1"
|
||||
}
|
||||
],
|
||||
"LocalDrives": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/Blade1/Drives/1"
|
||||
}
|
||||
],
|
||||
"RemoteDrives": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Services/RSS1/Targets/target1"
|
||||
}
|
||||
],
|
||||
"ManagedBy": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Managers/PODM"
|
||||
}
|
||||
],
|
||||
"Oem": {}
|
||||
},
|
||||
"Actions": {
|
||||
"#ComposedNode.Reset": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.Reset",
|
||||
"ResetType@Redfish.AllowableValues": [
|
||||
"On",
|
||||
"ForceOff",
|
||||
"GracefulRestart",
|
||||
"ForceRestart",
|
||||
"Nmi",
|
||||
"ForceOn",
|
||||
"PushPowerButton",
|
||||
"GracefulShutdown"
|
||||
]
|
||||
},
|
||||
"#ComposedNode.Assemble": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.Assemble"
|
||||
},
|
||||
"#ComposedNode.AttachResource": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.AttachResource",
|
||||
"@Redfish.ActionInfo": {
|
||||
"@odata.id":"/redfish/v1/Nodes/Node1/Actions/AttachResourceActionInfo"
|
||||
}
|
||||
},
|
||||
"#ComposedNode.DetachResource": {
|
||||
"target": "/redfish/v1/Nodes/Node1/Actions/ComposedNode.DetachResource",
|
||||
"@Redfish.ActionInfo": {
|
||||
"@odata.id":"/redfish/v1/Nodes/Node1/Actions/DetachResourceActionInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
rsd_virt_for_nova/tests/json_samples/node_col.json
Normal file
13
rsd_virt_for_nova/tests/json_samples/node_col.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#ComposedNodeCollection.ComposedNodeCollection",
|
||||
"@odata.id": "/redfish/v1/Nodes",
|
||||
"@odata.type": "#ComposedNodeCollection.ComposedNodeCollection",
|
||||
"Actions": {
|
||||
"#ComposedNodeCollection.Allocate": {
|
||||
"target": "/redfish/v1/Nodes/Actions/Allocate"
|
||||
}
|
||||
},
|
||||
"Members": [],
|
||||
"Members@odata.count": 0,
|
||||
"Name": "Composed Node Collection"
|
||||
}
|
44
rsd_virt_for_nova/tests/json_samples/root.json
Normal file
44
rsd_virt_for_nova/tests/json_samples/root.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#ServiceRoot.ServiceRoot",
|
||||
"@odata.id": "/redfish/v1/",
|
||||
"@odata.type": "#ServiceRoot.v1_1_1.ServiceRoot",
|
||||
"Id": "RootService",
|
||||
"Name": "Root Service",
|
||||
"Description": "description-as-string",
|
||||
"RedfishVersion": "1.1.0",
|
||||
"UUID": "92384634-2938-2342-8820-489239905423",
|
||||
"Systems": {
|
||||
"@odata.id": "/redfish/v1/Systems"
|
||||
},
|
||||
"Chassis": {
|
||||
"@odata.id": "/redfish/v1/Chassis"
|
||||
},
|
||||
"Managers": {
|
||||
"@odata.id": "/redfish/v1/Managers"
|
||||
},
|
||||
"EventService": {
|
||||
"@odata.id": "/redfish/v1/EventService"
|
||||
},
|
||||
"Fabrics": {
|
||||
"@odata.id": "/redfish/v1/Fabrics"
|
||||
},
|
||||
"EthernetSwitches": {
|
||||
"@odata.id": "/redfish/v1/EthernetSwitches"
|
||||
},
|
||||
"StorageServices": {
|
||||
"@odata.id": "/redfish/v1/StorageServices"
|
||||
},
|
||||
"Oem": {
|
||||
"Intel_RackScale": {
|
||||
"@odata.type": "#Intel.Oem.ServiceRoot",
|
||||
"ApiVersion": "2.3.0",
|
||||
"Nodes": {
|
||||
"@odata.id": "/redfish/v1/Nodes"
|
||||
},
|
||||
"EthernetSwitches": {
|
||||
"@odata.id": "/redfish/v1/EthernetSwitches"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Links": {}
|
||||
}
|
16
rsd_virt_for_nova/tests/json_samples/sys_collection.json
Normal file
16
rsd_virt_for_nova/tests/json_samples/sys_collection.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#ComputerSystemCollection.ComputerSystemCollection",
|
||||
"@odata.id": "/redfish/v1/Systems",
|
||||
"@odata.type": "#ComputerSystemCollection.ComputerSystemCollection",
|
||||
"Name": "Computer System Collection",
|
||||
"Description": "description-as-string",
|
||||
"Members@odata.count": 2,
|
||||
"Members": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System1"
|
||||
},
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Systems/System2"
|
||||
}
|
||||
]
|
||||
}
|
123
rsd_virt_for_nova/tests/json_samples/system.json
Normal file
123
rsd_virt_for_nova/tests/json_samples/system.json
Normal file
@ -0,0 +1,123 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#ComputerSystem.ComputerSystem", "@odata.id": "/redfish/v1/Systems/System1",
|
||||
"@odata.type": "#ComputerSystem.v1_3_0.ComputerSystem",
|
||||
"Id": "System1",
|
||||
"Name": "My Computer System",
|
||||
"Description": "Description of server",
|
||||
"SystemType": "Physical",
|
||||
"AssetTag": "free form asset tag",
|
||||
"Manufacturer": "Manufacturer Name",
|
||||
"Model": "Model Name",
|
||||
"SKU": "SKU",
|
||||
"SerialNumber": "2M220100SL",
|
||||
"PartNumber": "Computer1",
|
||||
"UUID": "00000000-0000-0000-0000-000000000000",
|
||||
"HostName": null,
|
||||
"Status": {
|
||||
"State": "Enabled",
|
||||
"Health": "OK",
|
||||
"HealthRollup": "OK"
|
||||
},
|
||||
"IndicatorLED": null,
|
||||
"PowerState": "On",
|
||||
"Boot": {
|
||||
"@odata.type": "#ComputerSystem.v1_1_0.Boot",
|
||||
"BootSourceOverrideEnabled": "Disabled",
|
||||
"BootSourceOverrideTarget": "None",
|
||||
"BootSourceOverrideTarget@Redfish.AllowableValues": ["None"],
|
||||
"BootSourceOverrideMode": null,
|
||||
"BootSourceOverrideMode@Redfish.AllowableValues": []
|
||||
},
|
||||
"BiosVersion": null,
|
||||
"ProcessorSummary": {
|
||||
"Count": 1,
|
||||
"Model": null,
|
||||
"Status": {
|
||||
"State": null,
|
||||
"Health": null,
|
||||
"HealthRollup": null
|
||||
}
|
||||
},
|
||||
"MemorySummary": {
|
||||
"TotalSystemMemoryGiB": 32,
|
||||
"Status": {
|
||||
"State": null,
|
||||
"Health": null,
|
||||
"HealthRollup": null
|
||||
}
|
||||
},
|
||||
"Processors": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Processors"
|
||||
},
|
||||
"EthernetInterfaces": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1/EthernetInterfaces"
|
||||
},
|
||||
"SimpleStorage": {},
|
||||
"Storage": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Storage"
|
||||
},
|
||||
"Memory": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Memory"
|
||||
},
|
||||
"PCIeDevices": [
|
||||
{
|
||||
"@odata.id": "/redfish/v1/Chassis/PCIeSwitch1/PCIeDevices/Device1"
|
||||
}
|
||||
],
|
||||
"PCIeFunctions": [],
|
||||
"TrustedModules": [],
|
||||
"Links": {
|
||||
"@odata.type": "#ComputerSystem.v1_2_0.Links",
|
||||
"Chassis": [{
|
||||
"@odata.id": "/redfish/v1/Chassis/4"
|
||||
}],
|
||||
"ManagedBy": [{
|
||||
"@odata.id": "/redfish/v1/Managers/1"
|
||||
}],
|
||||
"Endpoints": [],
|
||||
"Oem": {}
|
||||
},
|
||||
"Actions": {
|
||||
"#ComputerSystem.Reset": {
|
||||
"target": "/redfish/v1/Systems/System1/Actions/ComputerSystem.Reset",
|
||||
"ResetType@Redfish.AllowableValues": ["On",
|
||||
"ForceOff",
|
||||
"GracefulShutdown",
|
||||
"ForceRestart",
|
||||
"Nmi",
|
||||
"GracefulRestart",
|
||||
"ForceOn",
|
||||
"PushPowerButton"]
|
||||
},
|
||||
"Oem": {
|
||||
"#Intel.Oem.StartDeepDiscovery": {
|
||||
"target": "/redfish/v1/Systems/System1/Actions/Oem/Intel.Oem.StartDeepDiscovery"
|
||||
},
|
||||
"#Intel.Oem.StartDiscoveryOnDemand": {
|
||||
"target": "/redfish/v1/Systems/System1/Actions/Oem/Intel.Oem.StartDiscoveryOnDemand"
|
||||
},
|
||||
"#Intel.Oem.ChangeTPMState": {
|
||||
"target": "/redfish/v1/Systems/System1/Actions/Oem/Intel.Oem.ChangeTPMState",
|
||||
"InterfaceType@Redfish.AllowableValues": [
|
||||
"TPM1_2",
|
||||
"TPM2_0"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Oem": {
|
||||
"Intel_RackScale": {
|
||||
"@odata.type": "#Intel.Oem.ComputerSystem",
|
||||
"PciDevices": [],
|
||||
"DiscoveryState": "Basic",
|
||||
"ProcessorSockets": 1,
|
||||
"MemorySockets": 2,
|
||||
"PCIeConnectionId": [],
|
||||
"UserModeEnabled": false,
|
||||
"TrustedExecutionTechnologyEnabled": false,
|
||||
"Metrics": {
|
||||
"@odata.id": "/redfish/v1/Systems/System1/Metrics"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
rsd_virt_for_nova/tests/virt/__init__.py
Normal file
1
rsd_virt_for_nova/tests/virt/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Initialize tests for the virt driver."""
|
1
rsd_virt_for_nova/tests/virt/rsd/__init__.py
Normal file
1
rsd_virt_for_nova/tests/virt/rsd/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Initialize tests for the virt driver."""
|
854
rsd_virt_for_nova/tests/virt/rsd/test_driver.py
Normal file
854
rsd_virt_for_nova/tests/virt/rsd/test_driver.py
Normal file
@ -0,0 +1,854 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
"""Unit tests for the RSD virt driver."""
|
||||
|
||||
import json
|
||||
|
||||
import mock
|
||||
|
||||
from nova import context
|
||||
|
||||
from nova import exception
|
||||
|
||||
from nova import objects
|
||||
|
||||
from nova import rc_fields as fields
|
||||
|
||||
from nova.compute import power_state
|
||||
from nova.compute import provider_tree
|
||||
|
||||
from nova.objects import flavor
|
||||
|
||||
from nova.virt import fake
|
||||
from nova.virt import hardware
|
||||
|
||||
from rsd_virt_for_nova.conf import rsd as cfg
|
||||
|
||||
from rsd_virt_for_nova.virt import rsd
|
||||
from rsd_virt_for_nova.virt.rsd import driver
|
||||
|
||||
from oslo_utils import versionutils
|
||||
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
|
||||
from oslotest import base
|
||||
|
||||
import rsd_lib
|
||||
|
||||
from rsd_lib.resources.v2_1.chassis import chassis
|
||||
from rsd_lib.resources.v2_2.system import system
|
||||
|
||||
from rsd_lib.resources.v2_3.node import node
|
||||
from rsd_lib.resources.v2_3.node import node as v2_3_node
|
||||
|
||||
from sushy import connector
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class FakeInstance(object):
|
||||
"""A class to fake out nova instances."""
|
||||
|
||||
def __init__(self, name, state, uuid, new_flavor):
|
||||
"""Initialize the variables for fake instances."""
|
||||
self.name = name
|
||||
self.power_state = state
|
||||
self.uuid = uuid
|
||||
self.display_description = None
|
||||
self.flavor = new_flavor
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Method to retrieve fake instance variables."""
|
||||
return getattr(self, key)
|
||||
|
||||
def delete_node(self):
|
||||
"""Fake delete node function."""
|
||||
pass
|
||||
|
||||
def reset_node(self, action):
|
||||
"""Fake reset node function."""
|
||||
pass
|
||||
|
||||
|
||||
class FakeFlavor(object):
|
||||
"""A class to fake out a flavor for a nova instance."""
|
||||
|
||||
def __init__(self, vcpus, memory_mb, name, flavorid, extra_specs):
|
||||
"""Initialize the variables for a fake flavor."""
|
||||
self.vcpus = vcpus
|
||||
self.memory_mb = memory_mb
|
||||
self.name = name
|
||||
self.flavorid = flavorid
|
||||
self.extra_specs = extra_specs
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Method to retrieve fake flavor variables."""
|
||||
return getattr(self, key)
|
||||
|
||||
|
||||
class TestRSDDriver(base.BaseTestCase):
|
||||
"""A test class for the driver."""
|
||||
|
||||
@mock.patch.object(rsd, 'PODM_connection', autospaec=True)
|
||||
@mock.patch.object(connector, 'Connector', autospec=True)
|
||||
def setUp(self, mock_connector, pod_conn):
|
||||
"""Initial setup of mocks for all of the unit tests."""
|
||||
super(TestRSDDriver, self).setUp()
|
||||
# Mock out the connection to the RSD redfish API
|
||||
self.root_conn = mock.MagicMock()
|
||||
mock_connector.return_value = self.root_conn
|
||||
|
||||
# Create sample collections and instances of Chassis/System/Nodes
|
||||
with open('rsd_virt_for_nova/tests/json_samples/root.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
self.rsd = rsd_lib.main.RSDLib('http://foo.bar:8442', username='foo',
|
||||
password='bar', verify=False).factory()
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/chassis_col.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
self.chassis_col = chassis.ChassisCollection(
|
||||
self.root_conn, '/redfish/v1/Chassis',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/chassis.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
|
||||
self.chassis_inst = chassis.Chassis(
|
||||
self.root_conn, '/redfish/v1/Chassis/Chassis1',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/node_col.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
self.node_collection = node.NodeCollection(
|
||||
self.root_conn, '/redfish/v1/Nodes', redfish_version='1.0.2')
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/node.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
self.node_inst = node.Node(
|
||||
self.root_conn, '/redfish/v1/Nodes/Node1',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/node_assembled.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
self.node_ass_inst = node.Node(
|
||||
self.root_conn, '/redfish/v1/Nodes/Node1',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/sys_collection.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = \
|
||||
json.loads(f.read())
|
||||
self.system_col = system.SystemCollection(
|
||||
self.root_conn, '/redfish/v1/Systems',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
with open('rsd_virt_for_nova/tests/json_samples/system.json', 'r') as f:
|
||||
self.root_conn.get.return_value.json.return_value = json.loads(
|
||||
f.read())
|
||||
self.system_inst = system.System(
|
||||
self.root_conn, '/redfish/v1/Systems/System1',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
# Mock out a fake virt driver and its dependencies/parameters
|
||||
self.RSD = driver.RSDDriver(fake.FakeVirtAPI())
|
||||
|
||||
# Create Fake flavors and instances
|
||||
gb = self.system_inst.memory_summary.size_gib
|
||||
mem = self.RSD.conv_GiB_to_MiB(gb)
|
||||
proc = self.system_inst.processors.summary.count
|
||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
||||
res = fields.ResourceClass.normalize_name(self.system_inst.identity)
|
||||
spec = 'resources:' + res
|
||||
# Mock out some instances for testing
|
||||
self.flavor = FakeFlavor(
|
||||
gb, mem, str('RSD.' + flav_id),
|
||||
self.system_inst.identity,
|
||||
spec)
|
||||
self.inst1 = FakeInstance('inst1', power_state.RUNNING,
|
||||
'inst1id', self.flavor)
|
||||
self.invalid_inst = FakeInstance(
|
||||
'inv_inst', power_state.RUNNING, 'inv_inst_id',
|
||||
self.flavor)
|
||||
self.RSD.instances = {self.inst1.uuid: self.inst1}
|
||||
|
||||
# A provider tree for testing on the placement API
|
||||
self.ptree = provider_tree.ProviderTree()
|
||||
|
||||
self.test_image_meta = {
|
||||
"disk_format": "raw",
|
||||
}
|
||||
|
||||
@mock.patch.object(driver, 'set_nodes')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
||||
def test_init_nodes_success(self, check_chas_sys, set_nodes):
|
||||
"""Initialize nodes successful test."""
|
||||
# Setup for test to successfully create nodes for each valid chassis
|
||||
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
|
||||
chas_col.members_identities = ['/redfish/v1/Chassis/Chassis1']
|
||||
self.RSD._init_nodes()
|
||||
|
||||
# Confirm that the correct functionality is called and the correct
|
||||
# compute nodes are created to boot hypervisors from
|
||||
self.RSD.driver.podm_connection.assert_called()
|
||||
self.RSD.driver.PODM.get_chassis_collection.assert_called()
|
||||
chas_col.get_member.assert_called_with('/redfish/v1/Chassis/Chassis1')
|
||||
check_chas_sys.assert_called_with(chas_col.get_member.return_value)
|
||||
set_nodes.assert_called_with(['/redfish/v1/Chassis/Chassis1'])
|
||||
|
||||
@mock.patch.object(driver, 'set_nodes')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
||||
def test_init_nodes_failure(self, check_chas_sys, set_nodes):
|
||||
"""Initialize nodes failure test."""
|
||||
# Setup for test failing to create nodes for each chassis
|
||||
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
|
||||
self.RSD._init_nodes()
|
||||
|
||||
# Verify failed nodes and insufficient function calls
|
||||
self.RSD.driver.podm_connection.assert_called()
|
||||
self.RSD.driver.PODM.get_chassis_collection.assert_called()
|
||||
chas_col.get_member.assert_not_called()
|
||||
check_chas_sys.assert_not_called()
|
||||
set_nodes.assert_called_with([])
|
||||
|
||||
def test_init_host(self):
|
||||
"""Test initializing the host."""
|
||||
# Run test
|
||||
host = self.RSD.init_host(self.chassis_inst)
|
||||
|
||||
# Confirm the correct hostname is identified
|
||||
self.assertEqual(host, self.chassis_inst)
|
||||
|
||||
@mock.patch.object(hardware, 'InstanceInfo')
|
||||
def test_get_info_valid(self, info):
|
||||
"""Test getting information for a valid instance."""
|
||||
# Run test
|
||||
self.RSD.get_info(self.inst1)
|
||||
|
||||
# Confirm that the correct hardware info os collected
|
||||
info.assert_called_once_with(state=self.inst1.power_state)
|
||||
|
||||
@mock.patch.object(hardware, 'InstanceInfo')
|
||||
def test_get_info_invalid(self, info):
|
||||
"""Test getting information for an invalid instance."""
|
||||
# An invalid instance throws an exception, hardware info not requested
|
||||
self.assertRaises(
|
||||
exception.InstanceNotFound, self.RSD.get_info, self.invalid_inst)
|
||||
|
||||
info.assert_not_called()
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, '_init_nodes')
|
||||
def test_get_available_nodes_false_refresh(self, init_nodes):
|
||||
"""Test getting a list of the available nodes, no refresh."""
|
||||
# Run test checking the list of available nodes
|
||||
nodes = self.RSD.get_available_nodes(refresh=False)
|
||||
|
||||
# Confirm that the correst functions are called and all of the correct
|
||||
# nodes are available
|
||||
init_nodes.assert_called_once()
|
||||
self.assertEqual(nodes, self.RSD._nodes)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, '_init_nodes')
|
||||
def test_get_available_nodes_true_refresh(self, init_nodes):
|
||||
"""Test getting a list of the available nodes, with refresh."""
|
||||
# Run test checking the list of available nodes, refresh
|
||||
nodes = self.RSD.get_available_nodes(refresh=True)
|
||||
|
||||
# Confirm that the correst functions are called and all of the correct
|
||||
# nodes are available
|
||||
init_nodes.assert_called_once()
|
||||
self.assertEqual(nodes, self.RSD._nodes)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'get_available_nodes')
|
||||
def test_node_is_available_invalid(self, getNodes):
|
||||
"""Test if a node is available for an instance, failure."""
|
||||
# Run test checking a node is available
|
||||
avail = self.RSD.node_is_available(self.chassis_inst.identity)
|
||||
|
||||
# Confirm the correct functions are called and confirm that the
|
||||
# node being checked is not available
|
||||
getNodes.assert_called()
|
||||
self.assertEqual(self.RSD.instance_node, None)
|
||||
self.assertEqual(avail, False)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'get_available_nodes')
|
||||
def test_node_is_available_valid(self, getNodes):
|
||||
"""Test if a node is available for an instance, success."""
|
||||
# Run test checking a node is available
|
||||
# Setup mocks for a successful test
|
||||
getNodes.return_value = self.chassis_col.members_identities
|
||||
avail = self.RSD.node_is_available('/redfish/v1/Chassis/Chassis1')
|
||||
|
||||
# Confirm successful check that node is available through the correct
|
||||
# function calls
|
||||
getNodes.assert_called()
|
||||
self.assertEqual(self.RSD.instance_node,
|
||||
'/redfish/v1/Chassis/Chassis1')
|
||||
self.assertEqual(avail, True)
|
||||
|
||||
def test_list_instances(self):
|
||||
"""Test listing all instances."""
|
||||
# Run test to list available instances
|
||||
instances = self.RSD.list_instances()
|
||||
|
||||
# Confirm the result matches the internal list
|
||||
self.assertEqual(instances, {self.inst1.uuid: self.inst1})
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'power_on')
|
||||
def test_spawn_success(self, power_on):
|
||||
"""Test spawning an instance successfully."""
|
||||
# Mock out setup to successfully create a node
|
||||
node_col = self.RSD.driver.PODM.get_node_collection.return_value
|
||||
node_col.members_identities = ['/redfish/v1/Nodes/Node1']
|
||||
self.RSD.driver.PODM.get_node.return_value = self.node_ass_inst
|
||||
mock_context = context.get_admin_context()
|
||||
self.RSD.rsd_flavors = {self.flavor.flavorid: {
|
||||
'id': 'flav_id',
|
||||
'rsd_systems': [self.system_inst.identity]}}
|
||||
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
|
||||
# Run spawning test
|
||||
self.RSD.spawn(mock_context, self.inst1, image_meta,
|
||||
[], None, {})
|
||||
|
||||
# Confirm that a node instances is spawned and the physical composed
|
||||
# node is powered on
|
||||
self.RSD.driver.PODM.get_node_collection.assert_called_once()
|
||||
self.RSD.driver.PODM.get_node.assert_called_with(
|
||||
'/redfish/v1/Nodes/Node1')
|
||||
power_on.assert_called_once_with(mock_context, self.inst1, None)
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_destroy_success(self, mock_node):
|
||||
"""Test destroying an instance and deleting the composed node."""
|
||||
# Mock out instances and composed nodes for testing purposes
|
||||
node_collection = self.RSD.driver.PODM.get_node_collection
|
||||
node_inst = node_collection.return_value.compose_node.return_value
|
||||
self.RSD._composed_nodes = {self.inst1.uuid: mock_node}
|
||||
|
||||
# Try to destroy the instance
|
||||
self.RSD.destroy("context", self.inst1, network_info=None)
|
||||
|
||||
# Confirm that the instance has been delete from the list of instances
|
||||
mock_node.delete_node.assert_called_once()
|
||||
node_collection.assert_called_once()
|
||||
node_collection.return_value.compose_node.assert_called_once()
|
||||
node_inst.assemble_node.assert_called_once()
|
||||
self.assertNotIn(self.inst1.uuid, self.RSD.instances)
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_destory_failure(self, mock_node):
|
||||
"""Test failure to destroy a composed node instance."""
|
||||
# Mock out instances and composed nodes for testing purposes
|
||||
self.RSD._composed_nodes = {}
|
||||
node_collection = self.RSD.driver.PODM.get_node_collection
|
||||
node_inst = node_collection.return_value.compose_node.return_value
|
||||
# Try to destroy the instance
|
||||
self.RSD.destroy("context", self.inst1, network_info=None)
|
||||
|
||||
# Confirm that the instance failed to delete and a new node was not
|
||||
# created to replace it
|
||||
mock_node.delete_node.assert_not_called()
|
||||
self.RSD.driver.PODM.get_node_collection.assert_not_called()
|
||||
node_collection.return_value.compose_node.assert_not_called()
|
||||
node_inst.assemble_node.assert_not_called()
|
||||
self.assertNotIn(self.inst1.uuid, self.RSD.instances)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, '_create_flavors')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_flavors')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
||||
@mock.patch.object(versionutils, 'convert_version_to_int')
|
||||
@mock.patch.object(driver.RSDDriver, 'get_sys_proc_info')
|
||||
@mock.patch.object(driver.RSDDriver, 'get_sys_memory_info')
|
||||
def test_get_available_resource_success(self, mem_info, proc_info, conv_v,
|
||||
check_chas, check_flav,
|
||||
create_flav):
|
||||
"""Test successfully getting available resources for a node."""
|
||||
# Set up the parameters for the test
|
||||
chas_str = '/redfish/v1/Chassis/Chassis1'
|
||||
self.RSD._nodes = [chas_str]
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
|
||||
resources = self.RSD.get_available_resource(chas_str)
|
||||
|
||||
# Perform checks on all methods called on a successful run
|
||||
self.RSD.driver.PODM.get_chassis_collection.assert_called()
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
chas_col.get_member.assert_called_with(chas_str)
|
||||
check_chas.assert_called_with(chas_col.get_member.return_value)
|
||||
check_flav.assert_called_with(sys_col, sys_col.members_identities)
|
||||
create_flav.assert_called_once()
|
||||
mem_info.assert_called_with(check_chas.return_value)
|
||||
proc_info.assert_called_with(check_chas.return_value)
|
||||
conv_v.assert_called_with('1.0')
|
||||
self.assertEqual({'cpu_info': '',
|
||||
'disk_available_least': 0,
|
||||
'hypervisor_hostname': chas_str,
|
||||
'hypervisor_type': 'composable',
|
||||
'hypervisor_version': conv_v.return_value,
|
||||
'local_gb': 0,
|
||||
'local_gb_used': 0,
|
||||
'memory_mb': mem_info.return_value,
|
||||
'memory_mb_used': mem_info.return_value,
|
||||
'numa_topology': None,
|
||||
'supported_instances':
|
||||
[('x86_64', 'baremetal', 'hvm')],
|
||||
'vcpus': proc_info.return_value,
|
||||
'vcpus_used': proc_info.return_value}, resources)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, '_create_flavors')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_flavors')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
||||
@mock.patch.object(versionutils, 'convert_version_to_int')
|
||||
@mock.patch.object(driver.RSDDriver, 'get_sys_proc_info')
|
||||
@mock.patch.object(driver.RSDDriver, 'get_sys_memory_info')
|
||||
def test_get_available_resource_failure(self, mem_info, proc_info, conv_v,
|
||||
check_chas, check_flav,
|
||||
create_flav):
|
||||
"""Test failing to available resources for a node."""
|
||||
# If there is no composed node for the compute node = Failure
|
||||
self.RSD._nodes = []
|
||||
resources = self.RSD.get_available_resource(self.chassis_inst.identity)
|
||||
|
||||
# Confirm that there are no available resource to boot composed node
|
||||
# instances from
|
||||
self.RSD.driver.PODM.get_chassis_collection.assert_called_once()
|
||||
self.RSD.driver.PODM.get_system_collection.assert_not_called()
|
||||
check_chas.assert_not_called()
|
||||
mem_info.assert_not_called()
|
||||
proc_info.assert_not_called()
|
||||
conv_v.assert_not_called()
|
||||
check_flav.assert_not_called()
|
||||
create_flav.assert_not_called()
|
||||
self.assertEqual(resources, {})
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'create_child_inventory')
|
||||
@mock.patch.object(driver.RSDDriver, 'create_inventory')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
||||
def test_update_provider_tree_success(self, check_chas, create_inv,
|
||||
create_child_inv):
|
||||
"""Successfully updating the RP tree test."""
|
||||
# Setup a valid resource provider tree for the test
|
||||
self.ptree = provider_tree.ProviderTree()
|
||||
self.ptree.new_root('/redfish/v1/Chassis/Chassis1', uuids.cn)
|
||||
|
||||
# Setup other mocked calls for a successful test
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
sys_col.get_member.return_value = self.system_inst
|
||||
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
|
||||
chas_col.members_identities = ['/redfish/v1/Chassis/Chassis1']
|
||||
chas_col.get_member.return_value = self.chassis_inst
|
||||
check_chas.return_value = ['/redfish/v1/Systems/System1']
|
||||
self.RSD.update_provider_tree(self.ptree,
|
||||
'/redfish/v1/Chassis/Chassis1')
|
||||
|
||||
# Confirm that the provider tree for the placement API has been
|
||||
# updated correctly with a child node for each compute system available
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
self.RSD.driver.PODM.get_chassis_collection.assert_called()
|
||||
chas_col.get_member.assert_called_with('/redfish/v1/Chassis/Chassis1')
|
||||
check_chas.assert_called_with(self.chassis_inst)
|
||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
||||
create_child_inv.assert_called_once_with(self.system_inst.identity)
|
||||
create_inv.assert_called_once_with(check_chas.return_value)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'create_child_inventory')
|
||||
@mock.patch.object(driver.RSDDriver, 'create_inventory')
|
||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
||||
def test_update_provider_tree_failure(self, check_chas, create_inv,
|
||||
create_child_inv):
|
||||
"""Failing to update the RP tree test."""
|
||||
# Setup a valid resource provider tree for the test
|
||||
self.ptree = provider_tree.ProviderTree()
|
||||
|
||||
# Setup other mocked calls for a successful test
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
|
||||
self.RSD.update_provider_tree(self.ptree,
|
||||
'/redfish/v1/Chassis/Chassis1')
|
||||
|
||||
# Confirn that the provider tree for the placement API was not updated
|
||||
# correctly and no new nodes were created
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
self.RSD.driver.PODM.get_chassis_collection.assert_called()
|
||||
chas_col.get_member.assert_not_called()
|
||||
check_chas.assert_not_called()
|
||||
sys_col.get_member.assert_not_called()
|
||||
create_child_inv.assert_not_called()
|
||||
create_inv.assert_not_called()
|
||||
|
||||
def test_get_sys_proc_info_failure(self):
|
||||
"""Test failing to get sys_proc info."""
|
||||
# Set up a failing test for getting system processor information
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
cpus = self.RSD.get_sys_proc_info(None)
|
||||
|
||||
# Confirm that the relavant functions fail when called
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_not_called()
|
||||
self.assertEqual(cpus, 0)
|
||||
|
||||
def test_get_sys_proc_info_success(self):
|
||||
"""Test succeeding in getting sys_proc info."""
|
||||
# Set up for a successful test for getting system processor information
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
cpus = self.RSD.get_sys_proc_info(['/redfish/v1/Systems/System1'])
|
||||
|
||||
# Confirm that the relavant functions fail when called
|
||||
# And correct proccessor information is calculated
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
||||
self.assertEqual(cpus, self.system_inst.processors.summary.count)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
||||
def test_get_sys_memory_info_failure(self, conv_mem):
|
||||
"""Test failing to get sys_mem info."""
|
||||
# Set up a failing test for getting system memory information
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
mem_mb = self.RSD.get_sys_memory_info(None)
|
||||
|
||||
# Confirm that the relavant functions fail when called
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_not_called()
|
||||
conv_mem.assert_not_called()
|
||||
self.assertEqual(mem_mb, 0)
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
||||
def test_get_sys_memory_info_success(self, conv_mem):
|
||||
"""Test suceeding at getting sys_mem info."""
|
||||
# Set up mocks to successfully get memory information for a system
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
sys_col.members_identities = ['/redfish/v1/Systems/System1']
|
||||
sys_col.get_member.return_value = self.system_inst
|
||||
self.RSD.driver.composed_nodes = {
|
||||
self.node_inst.system.identity: self.node_inst.identity}
|
||||
# Run the test and get the result
|
||||
mem_mb = self.RSD.get_sys_memory_info(['/redfish/v1/Systems/System1'])
|
||||
|
||||
# Confirm that the relavant functions fail when called
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_called_once_with(
|
||||
'/redfish/v1/Systems/System1')
|
||||
conv_mem.assert_called_with(self.system_inst.memory_summary.size_gib)
|
||||
# Confirm the result is as to be expected
|
||||
self.assertEqual(
|
||||
mem_mb,
|
||||
conv_mem(self.system_inst.memory_summary.size_gib).__radd__())
|
||||
|
||||
def test_conv_GiB_to_MiB(self):
|
||||
"""Test the conversion of GiB to MiB."""
|
||||
# Run test on memory conversion function
|
||||
MiB = self.RSD.conv_GiB_to_MiB(8)
|
||||
|
||||
# Confirm the correct result is generated
|
||||
self.assertEqual(8191, MiB)
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_invalid_power_off(self, mock_node):
|
||||
"""Test the failed powering off of and instance."""
|
||||
# power off the invalid instance test
|
||||
self.RSD.power_off(self.invalid_inst)
|
||||
|
||||
# power state is not as it should be and the action function
|
||||
# reset is not called
|
||||
self.assertNotEqual(self.inst1.power_state, power_state.SHUTDOWN)
|
||||
mock_node.reset_node.assert_not_called()
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_valid_power_off(self, mock_node):
|
||||
"""Test the powering off of an instance."""
|
||||
# Mock out a node and instance to power off
|
||||
self.RSD._composed_nodes = {self.inst1.uuid: mock_node}
|
||||
|
||||
# Run power off test
|
||||
self.RSD.power_off(self.inst1)
|
||||
|
||||
# Confirm that the composed node instance is in the shutdown state
|
||||
self.assertEqual(self.inst1.power_state, power_state.SHUTDOWN)
|
||||
mock_node.reset_node.assert_called_once_with('graceful shutdown')
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_invalid_power_on(self, mock_node):
|
||||
"""Test the powering on of an invalid instance."""
|
||||
# Run power on test
|
||||
self.RSD.power_on(mock.MagicMock(), self.invalid_inst, 'network_info')
|
||||
|
||||
# No reset action is called on the node
|
||||
mock_node.reset_node.assert_not_called()
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_valid_power_on(self, mock_node):
|
||||
"""Test the powering on of an instance."""
|
||||
# Mock out instances and composed nodes for testing purposes
|
||||
self.RSD._composed_nodes = {self.inst1.uuid: mock_node}
|
||||
|
||||
# Power on a valid instance
|
||||
self.RSD.power_on(mock.MagicMock(), self.inst1, 'network_info')
|
||||
|
||||
# Confirm that the composed node instance is in the running state
|
||||
self.assertEqual(self.inst1.power_state, power_state.RUNNING)
|
||||
mock_node.reset_node.assert_called_once_with('force on')
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_invalid_reboot(self, mock_node):
|
||||
"""Test reboot of an invalid instance."""
|
||||
# Perform a hard reboot on an invalid node
|
||||
self.RSD.reboot(
|
||||
mock.MagicMock(), self.invalid_inst, 'network_info', 'HARD')
|
||||
|
||||
# No reset action is called
|
||||
mock_node.reset_node.assert_not_called()
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_valid_hard_reboot(self, mock_node):
|
||||
"""Test valid reboot of a composed node instance."""
|
||||
# Mock out instances and composed nodes for testing purposes
|
||||
self.RSD._composed_nodes = {self.inst1.uuid: mock_node}
|
||||
|
||||
# Perform a hard reboot on a valid node
|
||||
self.RSD.reboot(mock.MagicMock(), self.inst1, 'network_info', 'HARD')
|
||||
|
||||
# Confirm the correct reset action is called
|
||||
mock_node.reset_node.assert_called_with('force restart')
|
||||
|
||||
@mock.patch.object(v2_3_node, 'Node', autospec=True)
|
||||
def test_valid_soft_reboot(self, mock_node):
|
||||
"""Test valid reboot of a composed node instance."""
|
||||
# Mock out instances and composed nodes for testing purposes
|
||||
self.RSD._composed_nodes = {self.inst1.uuid: mock_node}
|
||||
|
||||
# Perform a soft reboot on a valid node
|
||||
self.RSD.reboot(mock.MagicMock(), self.inst1, 'network_info', 'SOFT')
|
||||
|
||||
# Confirm the correct reset action is called
|
||||
mock_node.reset_node.assert_called_with('graceful restart')
|
||||
|
||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
||||
@mock.patch.object(driver.RSDDriver, 'get_sys_memory_info')
|
||||
@mock.patch.object(driver.RSDDriver, 'get_sys_proc_info')
|
||||
def test_create_inventory_success(self, sys_proc_info, sys_mem_info,
|
||||
conv_mem):
|
||||
"""Test creating a inventory for a provider tree."""
|
||||
# Setup test to successfully create inventory
|
||||
sys_mem_info.return_value = self.system_inst.memory_summary.size_gib
|
||||
sys_proc_info.return_value = self.system_inst.processors.summary.count
|
||||
inv = self.RSD.create_inventory([self.system_inst.identity])
|
||||
|
||||
# Check that the correct functions are called and the inventory
|
||||
# is generated correctly
|
||||
sys_proc_info.assert_called()
|
||||
sys_mem_info.assert_called()
|
||||
self.assertEqual(inv, {'MEMORY_MB': {
|
||||
'reserved': 0,
|
||||
'total': sys_mem_info.return_value,
|
||||
'max_unit': sys_mem_info.return_value,
|
||||
'min_unit': 1,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1
|
||||
},
|
||||
'VCPU': {
|
||||
'reserved': 0,
|
||||
'total': 1,
|
||||
'max_unit': 1,
|
||||
'min_unit': 1,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1}
|
||||
})
|
||||
|
||||
@mock.patch.object(fields.ResourceClass, 'normalize_name')
|
||||
def test_create_child_inventory(self, norm_name):
|
||||
"""Test creating inventory for the child RP's."""
|
||||
# Set up a test to create the inventory for child resource providers
|
||||
child_inv = self.RSD.create_child_inventory(
|
||||
[self.system_inst.identity])
|
||||
|
||||
# Check that the correct functions are called and the inventory
|
||||
# is generated correctly
|
||||
norm_name.assert_called_once_with([self.system_inst.identity])
|
||||
self.assertEqual(child_inv, {norm_name.return_value: {
|
||||
'total': 1,
|
||||
'reserved': 0,
|
||||
'min_unit': 1,
|
||||
'max_unit': 1,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1,
|
||||
}
|
||||
})
|
||||
|
||||
def test_check_chassis_systems_invalid(self):
|
||||
"""Test checking the systems available through an invalid chassis."""
|
||||
# Error raied for invalid chassis system check
|
||||
self.assertRaises(
|
||||
AttributeError, self.RSD.check_chassis_systems,
|
||||
'invalid_chassis_instance')
|
||||
|
||||
def test_check_chassis_systems_valid(self):
|
||||
"""Test checking the systems available through a valid chassis."""
|
||||
# Run test to check available systems for a Chassis
|
||||
systems = self.RSD.check_chassis_systems(self.chassis_inst)
|
||||
|
||||
# Confirm that the generated list equals the systems linked to the
|
||||
# Chassis
|
||||
self.assertEqual(systems, ['/redfish/v1/Systems/System1',
|
||||
'/redfish/v1/Systems/System2',
|
||||
'/redfish/v1/Systems/System3',
|
||||
'/redfish/v1/Systems/System4'])
|
||||
|
||||
@mock.patch.object(context, 'get_admin_context')
|
||||
@mock.patch.object(flavor.Flavor, '_flavor_get_by_flavor_id_from_db')
|
||||
@mock.patch.object(flavor, '_flavor_create')
|
||||
@mock.patch.object(fields.ResourceClass, 'normalize_name')
|
||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
||||
def test_create_flavors_success(self, conv_mem, norm_name, flav_create,
|
||||
get_flav, admin_context):
|
||||
"""Test creation of new flavors for a System, success."""
|
||||
# Set up the mock objects for a sucessful creation test
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
sys_col.members_identities = ['/redfish/v1/Systems/System1']
|
||||
sys_col.get_member.return_value = self.system_inst
|
||||
# Run test
|
||||
self.RSD._create_flavors()
|
||||
|
||||
# Check the function calls for the test
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
||||
conv_mem.assert_called_with(self.system_inst.memory_summary.size_gib)
|
||||
norm_name.assert_called_with(self.system_inst.identity)
|
||||
admin_context.assert_called()
|
||||
|
||||
# Flavor creation call check
|
||||
spec = 'resources:' + norm_name.return_value
|
||||
mem = conv_mem.return_value - 512
|
||||
proc = self.system_inst.processors.summary.count
|
||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
||||
flav_create.assert_called_once_with(
|
||||
admin_context.return_value,
|
||||
{'name': 'RSD-' + flav_id,
|
||||
'flavorid': flav_id,
|
||||
'memory_mb': mem,
|
||||
'vcpus': self.system_inst.processors.summary.count,
|
||||
'root_gb': 0,
|
||||
'extra_specs': {spec: '1'}})
|
||||
get_flav.assert_not_called()
|
||||
|
||||
@mock.patch.object(context, 'get_admin_context')
|
||||
@mock.patch.object(flavor.Flavor, '_flavor_get_by_flavor_id_from_db')
|
||||
@mock.patch.object(flavor, '_flavor_create')
|
||||
@mock.patch.object(fields.ResourceClass, 'normalize_name')
|
||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
||||
def test_create_flavors_exists(self, conv_mem, norm_name, flav_create,
|
||||
get_flav, admin_context):
|
||||
"""Test failing to create a flavor that already exists."""
|
||||
# Set up mocks to ensure flavors fail to be created
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
sys_col.members_identities = ['/redfish/v1/Systems/System1']
|
||||
sys_col.get_member.return_value = self.system_inst
|
||||
spec = 'resources:' + norm_name.return_value
|
||||
mem = conv_mem.return_value - 512
|
||||
proc = self.system_inst.processors.summary.count
|
||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
||||
self.RSD.rsd_flavors = {flav_id: {
|
||||
'id': flav_create.return_value['id'],
|
||||
'rsd_systems': [self.system_inst.identity]
|
||||
}}
|
||||
flav_create.return_value = Exception
|
||||
# Run test to try and create new flavors based on available systems
|
||||
self.RSD._create_flavors()
|
||||
|
||||
# Confirm no new flavors have been created
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
||||
conv_mem.assert_called_with(self.system_inst.memory_summary.size_gib)
|
||||
norm_name.assert_called_with(self.system_inst.identity)
|
||||
admin_context.assert_called()
|
||||
|
||||
# Flavor creation call check
|
||||
flav_create.assert_called_once_with(
|
||||
admin_context.return_value,
|
||||
{'name': 'RSD-' + flav_id,
|
||||
'flavorid': flav_id,
|
||||
'memory_mb': mem,
|
||||
'vcpus': self.system_inst.processors.summary.count,
|
||||
'root_gb': 0,
|
||||
'extra_specs': {spec: '1'}})
|
||||
# Confirm that the flavor already exists
|
||||
get_flav.assert_called_once_with(admin_context.return_value, flav_id)
|
||||
|
||||
@mock.patch.object(flavor.Flavor, '_flavor_get_by_flavor_id_from_db')
|
||||
@mock.patch.object(flavor, '_flavor_create')
|
||||
@mock.patch.object(fields.ResourceClass, 'normalize_name')
|
||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
||||
def test_create_flavors_failure(self, conv_mem, norm_name, flav_create,
|
||||
flav_from_db):
|
||||
"""Test failing the creation of new flavors for a System."""
|
||||
# Setup for a failed flavor creation test
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
self.RSD._create_flavors()
|
||||
|
||||
# Verification of flavor creation failure and invalid function calls
|
||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
||||
sys_col.get_member.assert_not_called()
|
||||
conv_mem.assert_not_called()
|
||||
norm_name.assert_not_called()
|
||||
flav_create.assert_not_called()
|
||||
flav_from_db.assert_not_called()
|
||||
|
||||
@mock.patch.object(flavor, '_flavor_destroy')
|
||||
def test_check_flavors_failure(self, flav_destroy):
|
||||
"""Test for failing to check existing flavors."""
|
||||
# Setup for the test to fail to check flavors
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
self.RSD.check_flavors(self.system_col, [])
|
||||
|
||||
# Confirm that checking the available flavors failed
|
||||
sys_col.get_member.assert_not_called()
|
||||
flav_destroy.assert_not_called()
|
||||
|
||||
@mock.patch.object(flavor, '_flavor_destroy')
|
||||
def test_check_flavors_success(self, flav_destroy):
|
||||
"""Test for successfully checking existing flavors."""
|
||||
# Setup for check flavor that exists
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
sys_col.get_member.return_value = self.system_inst
|
||||
self.RSD.rsd_flavors = {
|
||||
'mock_flav_id': {'id': 'flav_id',
|
||||
'rsd_systems': [self.system_inst.identity]}}
|
||||
self.RSD.check_flavors(sys_col, ['/redfish/v1/Systems/System1'])
|
||||
|
||||
# Confirm the list of available flavors
|
||||
# No flavors need to be deleted
|
||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
||||
flav_destroy.assert_not_called()
|
||||
|
||||
@mock.patch.object(context, 'get_admin_context')
|
||||
@mock.patch.object(flavor, '_flavor_destroy')
|
||||
def test_check_flavors_success_update(self, flav_destroy, get_context):
|
||||
"""Test checking existing flavors, update/delete flavors, success."""
|
||||
# Setup a test for flavors that need to be delete in a flavor check
|
||||
sys_str = '/redfish/v1/Systems/System1'
|
||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
||||
self.RSD.rsd_flavors = {
|
||||
'mock_flav_id': {'id': 'flav_id',
|
||||
'rsd_systems': [sys_str]}}
|
||||
self.RSD.check_flavors(sys_col, [sys_str])
|
||||
|
||||
# Confirm the list of availbable flavors
|
||||
# Delete all flavors that no longer have associated systems
|
||||
sys_col.get_member.assert_called_with(sys_str)
|
||||
get_context.assert_called_once()
|
||||
flav_destroy.assert_called_with(get_context.return_value, 'flav_id')
|
||||
self.assertEqual(self.RSD.rsd_flavors, {})
|
1
rsd_virt_for_nova/virt/__init__.py
Normal file
1
rsd_virt_for_nova/virt/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Initialize virt driver."""
|
85
rsd_virt_for_nova/virt/rsd/__init__.py
Normal file
85
rsd_virt_for_nova/virt/rsd/__init__.py
Normal file
@ -0,0 +1,85 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Initialize the rsd virt driver."""
|
||||
|
||||
from rsd_virt_for_nova.conf import rsd as cfg
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
import rsd_lib
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PODM_connection(object):
|
||||
"""A class to make the connection to the PODM."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize class and connection."""
|
||||
self.PODM = None
|
||||
self._RSD_NODES = list()
|
||||
self.composed_nodes = {}
|
||||
self.PODM_UUID = None
|
||||
|
||||
def podm_connection(self):
|
||||
"""Establish/Refresh the connection to the PODM."""
|
||||
PODM_IP = CONF.rsd.podm_ip
|
||||
PODM_PORT = str(CONF.rsd.podm_port)
|
||||
PODM_URI = str('https://' + PODM_IP + ':' + PODM_PORT + '/redfish/v1')
|
||||
self.PODM = rsd_lib.RSDLib(PODM_URI, username=CONF.rsd.podm_user,
|
||||
password=CONF.rsd.podm_password,
|
||||
verify=False).factory()
|
||||
|
||||
podm_info = self.PODM.json
|
||||
if "UUID" in podm_info.keys():
|
||||
self.PODM_UUID = podm_info["UUID"]
|
||||
|
||||
# Gets the RSD compute systems to create compute agents from
|
||||
SYS_COL = self.PODM.get_system_collection()
|
||||
SYSTEMS = SYS_COL.members_identities
|
||||
|
||||
COMPOSED_NODE_COL = self.PODM.get_node_collection()
|
||||
|
||||
for s in SYSTEMS:
|
||||
# Allocate the resources for all of the systems availablea
|
||||
try:
|
||||
node = COMPOSED_NODE_COL.compose_node()
|
||||
except Exception as ex:
|
||||
LOG.warn("Node is already allocated: %s", ex)
|
||||
|
||||
# Set up the rsd hypervisor nodes.
|
||||
COMPOSED_NODE_COL = self.PODM.get_node_collection()
|
||||
COMPOSED_NODES = COMPOSED_NODE_COL.members_identities
|
||||
for cn in COMPOSED_NODES:
|
||||
# Assemble all the composed nodes
|
||||
try:
|
||||
node_inst = self.PODM.get_node(cn)
|
||||
node_inst.assemble_node()
|
||||
except Exception as a_ex:
|
||||
LOG.warn("Failed to assemble node: %s", a_ex)
|
||||
|
||||
try:
|
||||
node = COMPOSED_NODE_COL.get_member(cn)
|
||||
if node.system.identity not in self._RSD_NODES:
|
||||
self._RSD_NODES.append(node.system.identity)
|
||||
self.composed_nodes[node.system.identity] = cn
|
||||
|
||||
except Exception as ce:
|
||||
LOG.warn("Failed to establish a connection to the PODM.%s", ce)
|
||||
if cn in self.composed_nodes.keys():
|
||||
key = self.composed_nodes[cn]
|
||||
del self.composed_nodes[cn]
|
||||
self._RSD_NODES.remove(key)
|
456
rsd_virt_for_nova/virt/rsd/driver.py
Normal file
456
rsd_virt_for_nova/virt/rsd/driver.py
Normal file
@ -0,0 +1,456 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
"""
|
||||
An RSD hypervisor+api.
|
||||
|
||||
Inherits from Nova's FakeDriver to facilicate allocating/composing RSD
|
||||
nodes. Enables communication to the PODM to enable this.
|
||||
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
import json
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from nova import context
|
||||
|
||||
from nova import exception
|
||||
|
||||
from nova import rc_fields as fields
|
||||
|
||||
from nova.compute import power_state
|
||||
from nova.objects import fields as obj_fields
|
||||
from nova.objects import flavor
|
||||
from nova.virt import driver
|
||||
from nova.virt import hardware
|
||||
|
||||
from rsd_virt_for_nova.conf import rsd as cfg
|
||||
from rsd_virt_for_nova.virt import rsd
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from oslo_utils import versionutils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
PODM_NODE = ()
|
||||
|
||||
|
||||
def set_nodes(nodes):
|
||||
"""Set RSDDriver's node.list.
|
||||
|
||||
It has effect on the following methods:
|
||||
get_available_nodes()
|
||||
get_available_resource
|
||||
To restore the change, call restore_nodes()
|
||||
"""
|
||||
global PODM_NODE
|
||||
PODM_NODE = nodes
|
||||
|
||||
|
||||
class RSDDriver(driver.ComputeDriver):
|
||||
"""Implementation of nova compute driver to compose RSD nodes from nova."""
|
||||
|
||||
def __init__(self, virtapi, read_only=False):
|
||||
"""Initialize the RSDDriver."""
|
||||
super(RSDDriver, self).__init__(virtapi)
|
||||
# Initializes vairables to track compute nodes and instances
|
||||
self.driver = rsd.PODM_connection()
|
||||
self.instances = OrderedDict()
|
||||
self.rsd_flavors = OrderedDict()
|
||||
self._nodes = self._init_nodes()
|
||||
self._composed_nodes = OrderedDict()
|
||||
self.instance_node = None
|
||||
|
||||
def _init_nodes(self):
|
||||
"""Create a compute node for every compute sled."""
|
||||
self.driver.podm_connection()
|
||||
nodes = []
|
||||
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
||||
for c in CHASSIS_COL.members_identities:
|
||||
chas = CHASSIS_COL.get_member(c)
|
||||
cha_sys = self.check_chassis_systems(chas)
|
||||
if cha_sys != []:
|
||||
nodes.append(c)
|
||||
set_nodes(nodes)
|
||||
return copy.copy(PODM_NODE)
|
||||
|
||||
def init_host(self, host):
|
||||
"""Initialize anything that is necessary for the driver to function."""
|
||||
return host
|
||||
|
||||
def get_info(self, instance):
|
||||
"""Get instance info including power state."""
|
||||
if instance.uuid not in self.instances:
|
||||
raise exception.InstanceNotFound(instance_id=instance.uuid)
|
||||
i = self.instances[instance.uuid]
|
||||
return hardware.InstanceInfo(state=i.power_state)
|
||||
|
||||
def get_available_nodes(self, refresh=True):
|
||||
"""Return nodenames of all nodes managed by the compute service."""
|
||||
self._nodes = self._init_nodes()
|
||||
return self._nodes
|
||||
|
||||
def node_is_available(self, nodename):
|
||||
"""Return whether this compute service manages a particular node."""
|
||||
if nodename in self.get_available_nodes():
|
||||
self.instance_node = nodename
|
||||
return True
|
||||
# Refresh and check again.
|
||||
return nodename in self.get_available_nodes(refresh=True)
|
||||
|
||||
def list_instances(self):
|
||||
"""List all available instances."""
|
||||
return self.instances
|
||||
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, allocations, network_info=None,
|
||||
block_device_info=None):
|
||||
"""Spawn an RSD composed node on a boot request."""
|
||||
uuid = instance.uuid
|
||||
# Assembles the composed node and tracks node from the instance
|
||||
COMPOSED_NODE_COL = self.driver.PODM.get_node_collection()
|
||||
node_inst = None
|
||||
flav_id = instance.flavor.flavorid
|
||||
sys_list = self.rsd_flavors[flav_id]['rsd_systems']
|
||||
for n in COMPOSED_NODE_COL.members_identities:
|
||||
try:
|
||||
node_inst = self.driver.PODM.get_node(n)
|
||||
except Exception as ex:
|
||||
LOG.warn("Malformed composed node instance:%s", ex)
|
||||
|
||||
if node_inst is not None:
|
||||
node_state = node_inst.composed_node_state
|
||||
p_state = node_inst.power_state
|
||||
if node_state == 'assembled' and p_state == 'off':
|
||||
# Provide nova instance with composed node info
|
||||
node_sys_id = node_inst.system.identity
|
||||
if node_sys_id in sys_list:
|
||||
self.instances[uuid] = instance
|
||||
self._composed_nodes[uuid] = node_inst
|
||||
instance.display_description = \
|
||||
json.dumps({"node_identity": n,
|
||||
"node_uuid": node_inst.uuid})
|
||||
self.power_on(context, instance, network_info)
|
||||
return
|
||||
raise Exception("Failed to assign composed node for instance.")
|
||||
|
||||
def destroy(self, context, instance, network_info, block_device_info=None,
|
||||
destroy_disks=True):
|
||||
"""Destroy RSD composed node on nova delete request."""
|
||||
key = instance.uuid
|
||||
if key in self._composed_nodes:
|
||||
node_inst = self._composed_nodes[key]
|
||||
LOG.info("Disassembling RSD composed node.")
|
||||
node_inst.delete_node()
|
||||
|
||||
if key in self.instances:
|
||||
# Looks up the correct node to deallocate resources froma
|
||||
del self.instances[key]
|
||||
else:
|
||||
LOG.warning("Key not in instances.")
|
||||
|
||||
LOG.info("Reallocating resources for composed node.")
|
||||
COMPOSED_NODE_COL = self.driver.PODM.get_node_collection()
|
||||
try:
|
||||
node_inst = COMPOSED_NODE_COL.compose_node()
|
||||
node_inst.assemble_node()
|
||||
except Exception as ex:
|
||||
LOG.warn("Node is already allocated: %s", ex)
|
||||
else:
|
||||
if key in self.instances:
|
||||
# Looks up the correct node to deallocate resources from
|
||||
del self.instances[key]
|
||||
else:
|
||||
LOG.warning("Key '%(key)s' not in instances '%(inst)s'",
|
||||
{'key': key,
|
||||
'inst': self.instances}, instance=instance)
|
||||
|
||||
def get_available_resource(self, nodename):
|
||||
"""Update compute manager resource info on ComputeNode table."""
|
||||
cpu_info = ''
|
||||
if nodename not in self._nodes:
|
||||
return {}
|
||||
|
||||
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
||||
members = SYSTEM_COL.members_identities
|
||||
|
||||
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
||||
chas = CHASSIS_COL.get_member(nodename)
|
||||
cha_sys = self.check_chassis_systems(chas)
|
||||
|
||||
# Check if all flavors are valid
|
||||
self.check_flavors(SYSTEM_COL, members)
|
||||
self._create_flavors()
|
||||
res = {
|
||||
'vcpus': self.get_sys_proc_info(cha_sys),
|
||||
'memory_mb': self.get_sys_memory_info(cha_sys),
|
||||
'local_gb': 0,
|
||||
'vcpus_used': self.get_sys_proc_info(cha_sys),
|
||||
'memory_mb_used': self.get_sys_memory_info(cha_sys),
|
||||
'local_gb_used': 0,
|
||||
'hypervisor_type': 'composable',
|
||||
'hypervisor_version': versionutils.convert_version_to_int('1.0'),
|
||||
'hypervisor_hostname': nodename,
|
||||
'cpu_info': cpu_info,
|
||||
'disk_available_least': 0,
|
||||
'supported_instances': [(
|
||||
obj_fields.Architecture.X86_64,
|
||||
obj_fields.HVType.BAREMETAL,
|
||||
obj_fields.VMMode.HVM)],
|
||||
'numa_topology': None,
|
||||
}
|
||||
return res
|
||||
|
||||
def update_provider_tree(self, provider_tree, nodename, allocations=None):
|
||||
"""Update ProviderTree with current resources + inventory information.
|
||||
|
||||
When this method returns, provider_tree should represent the correct
|
||||
hierarchy of nested resource providers associated with this compute
|
||||
node, as well as the inventory, aggregates, and traits associated with
|
||||
those resource providers.
|
||||
This method supersedes get_inventory(): if this method is implemented,
|
||||
get_inventory() is not used.
|
||||
:note: Renaming a provider (by deleting it from provider_tree and
|
||||
re-adding it with a different name) is not supported at this time.
|
||||
See the developer reference documentation for more details:
|
||||
https://docs.openstack.org/nova/latest/reference/update-provider-tree.html
|
||||
"""
|
||||
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
||||
sys_s = SYSTEM_COL.members_identities
|
||||
systems = []
|
||||
for s in sys_s:
|
||||
systems.append(s)
|
||||
|
||||
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
||||
|
||||
for c in CHASSIS_COL.members_identities:
|
||||
chas = CHASSIS_COL.get_member(nodename)
|
||||
cha_sys = self.check_chassis_systems(chas)
|
||||
if cha_sys != []:
|
||||
for s in cha_sys:
|
||||
system = SYSTEM_COL.get_member(s)
|
||||
sys_inv = self.create_child_inventory(system.identity)
|
||||
try:
|
||||
provider_tree.new_child(s, nodename)
|
||||
except Exception as ex:
|
||||
LOG.warn("Failed to create new RP: %s", ex)
|
||||
provider_tree.update_inventory(s, sys_inv)
|
||||
chas_inv = self.create_inventory(cha_sys)
|
||||
provider_tree.update_inventory(nodename, chas_inv)
|
||||
|
||||
def get_sys_proc_info(self, systems):
|
||||
"""Track vcpus made available by the PODM."""
|
||||
cpus = 1
|
||||
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
||||
try:
|
||||
cpus = 0
|
||||
for s in systems:
|
||||
ss = SYSTEM_COL.get_member(s)
|
||||
if ss.identity in self.driver.composed_nodes.keys():
|
||||
cpus = cpus + ss.processors.summary.count
|
||||
except Exception as ex:
|
||||
LOG.info("Failed to get processor info: %s", ex)
|
||||
return cpus
|
||||
|
||||
def get_sys_memory_info(self, systems):
|
||||
"""Track memory available in the PODM."""
|
||||
ram = 1
|
||||
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
||||
try:
|
||||
ram = 0
|
||||
for s in systems:
|
||||
ss = SYSTEM_COL.get_member(s)
|
||||
if ss.identity in self.driver.composed_nodes.keys():
|
||||
mem = ss.memory_summary.size_gib
|
||||
ram = \
|
||||
ram + self.conv_GiB_to_MiB(mem)
|
||||
except Exception as ex:
|
||||
LOG.info("Failed to get memory info: %s", ex)
|
||||
return ram
|
||||
|
||||
def conv_GiB_to_MiB(self, input_GiB):
|
||||
"""Convert gib to mib."""
|
||||
return int(input_GiB / 0.000976562500000003)
|
||||
|
||||
def power_off(self, instance, timeout=0, retry_interval=0):
|
||||
"""Power off the specified instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param timeout: time to wait for GuestOS to shutdown
|
||||
:param retry_interval: How often to signal guest while
|
||||
waiting for it to shutdown
|
||||
"""
|
||||
key = instance.uuid
|
||||
timeout = timeout or None
|
||||
if key in self.instances and key in self._composed_nodes:
|
||||
LOG.info("Powering off composed node: %s", key)
|
||||
self.instances[instance.uuid].power_state = power_state.SHUTDOWN
|
||||
node_inst = self._composed_nodes[key]
|
||||
node_inst.reset_node('graceful shutdown')
|
||||
|
||||
def power_on(self, context, instance, network_info,
|
||||
block_device_info=None):
|
||||
"""Power on the specified instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
key = instance.uuid
|
||||
if key in self.instances and key in self._composed_nodes:
|
||||
LOG.info("Powering on composed node: %s", key)
|
||||
self.instances[instance.uuid].power_state = power_state.RUNNING
|
||||
node_inst = self._composed_nodes[key]
|
||||
node_inst.reset_node('force on')
|
||||
|
||||
def reboot(self, context, instance, network_info, reboot_type,
|
||||
block_device_info=None, bad_volumes_callback=None):
|
||||
"""Reboot the specified instance.
|
||||
|
||||
After this is called successfully, the instance's state
|
||||
goes back to power_state.RUNNING. The virtualization
|
||||
platform should ensure that the reboot action has completed
|
||||
successfully even in cases in which the underlying domain/vm
|
||||
is paused or halted/stopped.
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param network_info: instance network information
|
||||
:param reboot_type: Either a HARD or SOFT reboot
|
||||
:param block_device_info: Info pertaining to attached volumes
|
||||
:param bad_volumes_callback: Function to handle any bad volumes
|
||||
encountered
|
||||
"""
|
||||
key = instance.uuid
|
||||
if key in self.instances and key in self._composed_nodes:
|
||||
node_inst = self._composed_nodes[key]
|
||||
if reboot_type == 'HARD':
|
||||
LOG.info(
|
||||
"Force restart composed node for a hard reboot: %s", key)
|
||||
node_inst.reset_node('force restart')
|
||||
else:
|
||||
LOG.info("Graceful restart composed node for reboot: %s", key)
|
||||
node_inst.reset_node('graceful restart')
|
||||
|
||||
def create_inventory(self, system):
|
||||
"""Function to create provider tree inventory."""
|
||||
mem_max = 1
|
||||
proc_max = 1
|
||||
if self.get_sys_memory_info(system) >= mem_max:
|
||||
mem_max = self.get_sys_memory_info(system)
|
||||
if self.get_sys_proc_info(system) >= proc_max:
|
||||
proc_max = self.get_sys_proc_info(system)
|
||||
|
||||
return {
|
||||
'VCPU': {
|
||||
'total': proc_max,
|
||||
'max_unit': proc_max,
|
||||
'min_unit': 1,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1,
|
||||
'reserved': 0
|
||||
},
|
||||
'MEMORY_MB': {
|
||||
'total': mem_max,
|
||||
'max_unit': mem_max,
|
||||
'min_unit': 1,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1,
|
||||
'reserved': 0
|
||||
}
|
||||
}
|
||||
|
||||
def create_child_inventory(self, system):
|
||||
"""Create custom resources for all of the child RP's."""
|
||||
res = fields.ResourceClass.normalize_name(system)
|
||||
return {
|
||||
res: {
|
||||
'total': 1,
|
||||
'reserved': 0,
|
||||
'min_unit': 1,
|
||||
'max_unit': 1,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1,
|
||||
}
|
||||
}
|
||||
|
||||
def check_chassis_systems(self, chassis):
|
||||
"""Check the chassis for linked systems."""
|
||||
systems = chassis.json['Links']['ComputerSystems']
|
||||
cha_sys = []
|
||||
for s in systems:
|
||||
cha_sys += s.values()
|
||||
return cha_sys
|
||||
|
||||
def _create_flavors(self):
|
||||
"""Auto-generate the flavors for the compute systems available."""
|
||||
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
||||
for s in SYSTEM_COL.members_identities:
|
||||
sys = SYSTEM_COL.get_member(s)
|
||||
mem = self.conv_GiB_to_MiB(sys.memory_summary.size_gib) - 512
|
||||
proc = sys.processors.summary.count
|
||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
||||
res = fields.ResourceClass.normalize_name(sys.identity)
|
||||
spec = 'resources:' + res
|
||||
values = {
|
||||
'name': 'RSD-' + flav_id,
|
||||
'flavorid': flav_id,
|
||||
'memory_mb': mem,
|
||||
'vcpus': proc,
|
||||
'root_gb': 0,
|
||||
'extra_specs': {
|
||||
spec: '1'}
|
||||
}
|
||||
if sys.identity not in self.rsd_flavors:
|
||||
try:
|
||||
LOG.debug("New flavor for system: %s", sys.identity)
|
||||
rsd_flav = flavor._flavor_create(
|
||||
context.get_admin_context(), values)
|
||||
self.rsd_flavors[flav_id] = {
|
||||
'id': rsd_flav['id'],
|
||||
'rsd_systems': [sys.identity]
|
||||
}
|
||||
except Exception as ex:
|
||||
LOG.debug(
|
||||
"A flavor already exists for this rsd system: %s", ex)
|
||||
ex_flav = flavor.Flavor._flavor_get_by_flavor_id_from_db(
|
||||
context.get_admin_context(), flav_id)
|
||||
if flav_id not in self.rsd_flavors.keys():
|
||||
self.rsd_flavors[flav_id] = {
|
||||
'id': ex_flav['id'],
|
||||
'rsd_systems': [sys.identity]
|
||||
}
|
||||
else:
|
||||
sys_list = self.rsd_flavors[flav_id]['rsd_systems']
|
||||
sys_list.append(sys.identity)
|
||||
self.rsd_flavors[flav_id]['rsd_systems'] = sys_list
|
||||
|
||||
def check_flavors(self, collection, systems):
|
||||
"""Check if flavors should be deleted based on system removal."""
|
||||
sys_ids = []
|
||||
LOG.debug("Checking existing flavors.")
|
||||
for s in systems:
|
||||
sys = collection.get_member(s)
|
||||
sys_ids.append(sys.identity)
|
||||
|
||||
for k in list(self.rsd_flavors):
|
||||
sys_list = self.rsd_flavors[k]['rsd_systems']
|
||||
for s in sys_list:
|
||||
if s not in sys_ids:
|
||||
rsd_id = self.rsd_flavors[k]['id']
|
||||
flavor._flavor_destroy(context.get_admin_context(), rsd_id)
|
||||
LOG.debug("Deleting flavor for removed systems: %s", k)
|
||||
del self.rsd_flavors[k]
|
||||
return
|
18
test-requirements.txt
Normal file
18
test-requirements.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
mock>=2.0.0 # BSD
|
||||
PyMySQL>=0.7.6 # MIT License
|
||||
oslotest>=3.2.0 # Apache-2.0
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
sphinx>=1.6.2 # BSD
|
||||
stestr>=1.0.0 # Apache-2.0
|
||||
flake8
|
||||
pycodestyle
|
||||
wsgi-intercept>=1.4.1 # MIT License
|
||||
sushy>=1.7.0 # Apache-2.0
|
||||
|
||||
-e git+https://github.com/openstack/nova.git#egg=nova # Apache-2.0
|
53
tools/tox_install.sh
Executable file
53
tools/tox_install.sh
Executable file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Library constraint file contains this library version pin that is in conflict
|
||||
# with installing the library from source.
|
||||
|
||||
ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner
|
||||
BRANCH_NAME=master
|
||||
LIB_NAMES=(nova-rsd nova)
|
||||
requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?)
|
||||
|
||||
set -e
|
||||
|
||||
CONSTRAINTS_FILE=$1
|
||||
shift
|
||||
|
||||
install_cmd="pip install"
|
||||
mydir=$(mktemp -dt "nova-rsd-tox_install-XXXXXXX")
|
||||
trap "rm -rf $mydir" EXIT
|
||||
localfile=$mydir/upper-constraints.txt
|
||||
if [[ $CONSTRAINTS_FILE != http* ]]; then
|
||||
CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
|
||||
fi
|
||||
curl $CONSTRAINTS_FILE -k -o $localfile
|
||||
install_cmd="$install_cmd -c$localfile"
|
||||
|
||||
if [ $requirements_installed -eq 0 ]; then
|
||||
echo "Requirements already installed; using existing package"
|
||||
elif [ -x "$ZUUL_CLONER" ]; then
|
||||
pushd $mydir
|
||||
$ZUUL_CLONER --cache-dir \
|
||||
/opt/git \
|
||||
--branch $BRANCH_NAME \
|
||||
git://git.openstack.org \
|
||||
openstack/requirements
|
||||
cd openstack/requirements
|
||||
$install_cmd -e .
|
||||
popd
|
||||
else
|
||||
if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then
|
||||
REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements"
|
||||
fi
|
||||
$install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION}
|
||||
fi
|
||||
|
||||
# This is the main purpose of the script: Allow local installation of
|
||||
# the current repo. It is listed in constraints file and thus any
|
||||
# install will be constrained and we need to unconstrain it.
|
||||
for LIB_NAME in "${LIB_NAMES[@]}"; do
|
||||
edit-constraints $localfile $LIB_NAME
|
||||
done
|
||||
|
||||
$install_cmd -U $*
|
||||
exit $?
|
62
tox.ini
Normal file
62
tox.ini
Normal file
@ -0,0 +1,62 @@
|
||||
# tox (https://tox.readthedocs.io/) is a tool for running tests
|
||||
# in multiple virtualenvs. This configuration file will run the
|
||||
# test suite on all supported python versions. To use it, "pip install tox"
|
||||
# and then run "tox" from this directory.
|
||||
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
envlist = py27,py36,pycodestyle
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
basepython = python3
|
||||
usedevelop = True
|
||||
install_command = pip install {opts} {packages}
|
||||
#{toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHONWARNINGS=default::DeprecationWarning
|
||||
OS_STDOUT_CAPTURE=1
|
||||
OS_STDERR_CAPTURE=1
|
||||
OS_TEST_TIMEOUT=60
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
|
||||
[testenv:pycodestyle]
|
||||
commands =
|
||||
flake8 {posargs}
|
||||
pycodestyle {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHON=coverage run --source rsd_virt_for_nova --parallel-mode
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:py27]
|
||||
basepython = python2.7
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
|
||||
[testenv:py36]
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
|
||||
[flake8]
|
||||
ignore = E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E251,H405,N342
|
||||
exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,tools/xenserver*,releasenotes,*openstack/common*,*src,*/src
|
||||
|
||||
[pycodestyle]
|
||||
ignore = E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E251,H405,N342
|
||||
exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,tools/xenserver*,releasenotes,*openstack/common*,*src,*/src
|
Loading…
x
Reference in New Issue
Block a user