Retire oswin-tempest-plugin: remove repo content
Winstackers project has been retired - https://review.opendev.org/c/openstack/governance/+/886880 this commit removes the content of oswin-tempest-plugin deliverables of this project Change-Id: I489d36a539cb943c1d7216390e469a471719a2cf
This commit is contained in:
parent
9e95d33d9e
commit
0976964257
@ -1,6 +0,0 @@
|
|||||||
[run]
|
|
||||||
branch = True
|
|
||||||
source = oswin_tempest_plugin
|
|
||||||
|
|
||||||
[report]
|
|
||||||
ignore_errors = True
|
|
56
.gitignore
vendored
56
.gitignore
vendored
@ -1,56 +0,0 @@
|
|||||||
*.py[cod]
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Packages
|
|
||||||
*.egg*
|
|
||||||
*.egg-info
|
|
||||||
dist
|
|
||||||
build
|
|
||||||
eggs
|
|
||||||
parts
|
|
||||||
bin
|
|
||||||
var
|
|
||||||
sdist
|
|
||||||
develop-eggs
|
|
||||||
.installed.cfg
|
|
||||||
lib
|
|
||||||
lib64
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
cover/
|
|
||||||
.coverage*
|
|
||||||
!.coveragerc
|
|
||||||
.tox
|
|
||||||
nosetests.xml
|
|
||||||
.stestr
|
|
||||||
.venv
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
|
|
||||||
# Mr Developer
|
|
||||||
.mr.developer.cfg
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
|
|
||||||
# Complexity
|
|
||||||
output/*.html
|
|
||||||
output/*/index.html
|
|
||||||
|
|
||||||
# Sphinx
|
|
||||||
doc/build
|
|
||||||
|
|
||||||
# pbr generates these
|
|
||||||
AUTHORS
|
|
||||||
ChangeLog
|
|
||||||
|
|
||||||
# Editors
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
||||||
.*sw?
|
|
||||||
|
|
3
.mailmap
3
.mailmap
@ -1,3 +0,0 @@
|
|||||||
# Format is:
|
|
||||||
# <preferred e-mail> <other e-mail 1>
|
|
||||||
# <preferred e-mail> <other e-mail 2>
|
|
@ -1,3 +0,0 @@
|
|||||||
[DEFAULT]
|
|
||||||
test_path=${OS_TEST_PATH:-./oswin_tempest_plugin/tests}
|
|
||||||
top_dir=./
|
|
@ -1,5 +0,0 @@
|
|||||||
- project:
|
|
||||||
templates:
|
|
||||||
- openstack-python3-zed-jobs
|
|
||||||
- check-requirements
|
|
||||||
queue: os-win
|
|
@ -1,19 +0,0 @@
|
|||||||
The source repository for this project can be found at:
|
|
||||||
|
|
||||||
https://opendev.org/openstack/oswin-tempest-plugin
|
|
||||||
|
|
||||||
Pull requests submitted through GitHub are not monitored.
|
|
||||||
|
|
||||||
To start contributing to OpenStack, follow the steps in the contribution guide
|
|
||||||
to set up and use Gerrit:
|
|
||||||
|
|
||||||
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
|
|
||||||
|
|
||||||
Bugs should be filed on Launchpad:
|
|
||||||
|
|
||||||
https://bugs.launchpad.net/oswin-tempest-plugin
|
|
||||||
|
|
||||||
For more specific information about contributing to this repository, see the
|
|
||||||
oswin-tempest-plugin contributor guide:
|
|
||||||
|
|
||||||
https://docs.openstack.org/oswin-tempest-plugin/latest/contributor/contributing.html
|
|
@ -1,4 +0,0 @@
|
|||||||
oswin-tempest-plugin Style Commandments
|
|
||||||
=======================================
|
|
||||||
|
|
||||||
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
|
|
176
LICENSE
176
LICENSE
@ -1,176 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
25
README.rst
25
README.rst
@ -1,19 +1,10 @@
|
|||||||
====================
|
This project is no longer maintained.
|
||||||
oswin-tempest-plugin
|
|
||||||
====================
|
|
||||||
|
|
||||||
This project contains Tempest tests to cover the os_win project, as well as a plugin to automatically load these tests into Tempest.
|
The contents of this repository are still available in the Git
|
||||||
|
source code management system. To see the contents of this
|
||||||
|
repository before it reached its end of life, please check out the
|
||||||
|
previous commit with "git checkout HEAD^1".
|
||||||
|
|
||||||
Please fill here a long description which must be at least 3 lines wrapped on
|
For any further questions, please email
|
||||||
80 cols, so that distribution package maintainers can use it in their packages.
|
openstack-discuss@lists.openstack.org or join #openstack-dev on
|
||||||
Note that this is a hard requirement.
|
OFTC.
|
||||||
|
|
||||||
* Free software: Apache license
|
|
||||||
* Documentation: http://docs.openstack.org/developer/oswin-tempest-plugin
|
|
||||||
* Source: http://opendev.org/openstack/oswin-tempest-plugin
|
|
||||||
* Bugs: http://bugs.launchpad.net/oswin-tempest-plugin
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
* TODO
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
DIR_OSWIN_TEMPEST_PLUGIN=$DEST/oswin-tempest-plugin
|
|
||||||
|
|
||||||
if [[ "$1" == "stack" && "$2" == "install" ]]; then
|
|
||||||
cd $DIR_OSWIN_TEMPEST_PLUGIN
|
|
||||||
echo "Installing oswin-tempest-plugin"
|
|
||||||
setup_develop $DIR_OSWIN_TEMPEST_PLUGIN
|
|
||||||
|
|
||||||
echo "Sucessfully installed oswin-tempest-plugin"
|
|
||||||
|
|
||||||
fi
|
|
@ -1,2 +0,0 @@
|
|||||||
sphinx>=2.0.0,!=2.1.0 # BSD
|
|
||||||
openstackdocstheme>=2.2.1 # Apache-2.0
|
|
@ -1,82 +0,0 @@
|
|||||||
# -*- 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.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
|
||||||
# -- General configuration ----------------------------------------------------
|
|
||||||
|
|
||||||
# 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',
|
|
||||||
#'sphinx.ext.intersphinx',
|
|
||||||
'openstackdocstheme'
|
|
||||||
]
|
|
||||||
|
|
||||||
# openstackdocstheme options
|
|
||||||
openstackdocs_repo_name = 'openstack/oswin-tempest-plugin'
|
|
||||||
openstackdocs_auto_name = False
|
|
||||||
openstackdocs_bug_project = 'os-win'
|
|
||||||
openstackdocs_bug_tag = ''
|
|
||||||
|
|
||||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
|
||||||
# text edit cycles.
|
|
||||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = 'oswin-tempest-plugin'
|
|
||||||
copyright = '2017, Cloudbase Solutions'
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
add_module_names = True
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'native'
|
|
||||||
|
|
||||||
# -- Options for HTML output --------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
|
||||||
# html_theme_path = ["."]
|
|
||||||
# html_theme = '_theme'
|
|
||||||
# html_static_path = ['static']
|
|
||||||
html_theme = 'openstackdocs'
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = '%sdoc' % project
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass
|
|
||||||
# [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('index',
|
|
||||||
'%s.tex' % project,
|
|
||||||
'%s Documentation' % project,
|
|
||||||
'Cloudbase Solutions', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
|
||||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
|
@ -1,93 +0,0 @@
|
|||||||
============
|
|
||||||
Contributing
|
|
||||||
============
|
|
||||||
|
|
||||||
For general information on contributing to OpenStack, please check out the
|
|
||||||
`contributor guide <https://docs.openstack.org/contributors/>`_ to get started.
|
|
||||||
It covers all the basics that are common to all OpenStack projects: the accounts
|
|
||||||
you need, the basics of interacting with our Gerrit review system, how we
|
|
||||||
communicate as a community, etc.
|
|
||||||
|
|
||||||
Below will cover the more project specific information you need to get started
|
|
||||||
with oswin-tempest-plugin.
|
|
||||||
|
|
||||||
Communication
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
.. This would be a good place to put the channel you chat in as a project; when/
|
|
||||||
where your meeting is, the tags you prepend to your ML threads, etc.
|
|
||||||
|
|
||||||
We recommend using the standard communication channels, such as the OpenStack
|
|
||||||
mailing list or IRC channels. The official IRC channel (#openstack-hyper-v) is
|
|
||||||
not archived at the moment, so we recommend using #openstack-dev.
|
|
||||||
|
|
||||||
Please include one of the following tags when using the OpenStack mailing
|
|
||||||
list:
|
|
||||||
|
|
||||||
* winstackers
|
|
||||||
* windows
|
|
||||||
* hyper-v
|
|
||||||
|
|
||||||
Feel free to reach out to the Winstackers PTL or other core members.
|
|
||||||
|
|
||||||
Contacting the Core Team
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
.. This section should list the core team, their irc nicks, emails, timezones
|
|
||||||
etc. If all this info is maintained elsewhere (i.e. a wiki), you can link to
|
|
||||||
that instead of enumerating everyone here.
|
|
||||||
|
|
||||||
The Winstackers core team is composed of:
|
|
||||||
|
|
||||||
* Lucian Petrut <lpetrut@cloudbasesolutions.com> (lpetrut)
|
|
||||||
* Claudiu Belu <cbelu@cloudbasesolutions.com> (claudiub)
|
|
||||||
* Alessandro Pilotti <apilotti@cloudbasesolutions.com> (apilotti)
|
|
||||||
|
|
||||||
New Feature Planning
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
.. This section is for talking about the process to get a new feature in. Some
|
|
||||||
projects use blueprints, some want specs, some want both! Some projects
|
|
||||||
stick to a strict schedule when selecting what new features will be reviewed
|
|
||||||
for a release.
|
|
||||||
|
|
||||||
If you want to propose a new feature, we recommend `filing a blueprint
|
|
||||||
<https://blueprints.launchpad.net/oswin-tempest-plugin>`__ and then contacting
|
|
||||||
the core team.
|
|
||||||
|
|
||||||
Once the feature is approved, please propose the patches on Gerrit, following
|
|
||||||
the Openstack contributor guide.
|
|
||||||
|
|
||||||
Task Tracking
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
.. This section is about where you track tasks- launchpad? storyboard? is there
|
|
||||||
more than one launchpad project? what's the name of the project group in
|
|
||||||
storyboard?
|
|
||||||
|
|
||||||
We track our tasks in `Launchpad <https://bugs.launchpad.net/oswin-tempest-plugin>`__.
|
|
||||||
|
|
||||||
Reporting a Bug
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
.. Pretty self explanatory section, link directly to where people should report
|
|
||||||
bugs for your project.
|
|
||||||
|
|
||||||
You found an issue and want to make sure we are aware of it? You can do so on
|
|
||||||
`Launchpad <https://bugs.launchpad.net/oswin-tempest-plugin/+filebug>`__.
|
|
||||||
More info about Launchpad usage can be found on `OpenStack docs page
|
|
||||||
<https://docs.openstack.org/contributors/common/task-tracking.html#launchpad>`_.
|
|
||||||
|
|
||||||
Getting Your Patch Merged
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
.. This section should have info about what it takes to get something merged. Do
|
|
||||||
you require one or two +2's before +W? Do some of your repos require unit
|
|
||||||
test changes with all patches? etc.
|
|
||||||
|
|
||||||
Changes proposed to oswin-tempest-plugin generally require two ``Code-Review +2``
|
|
||||||
votes from oswin-tempest-plugin core reviewers before merging. In case of trivial
|
|
||||||
patches and urgent bug fixes, this rule is sometimes ignored.
|
|
||||||
|
|
||||||
Project Team Lead Duties
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
.. this section is where you can put PTL specific duties not already listed in
|
|
||||||
the common PTL guide (linked below), or if you already have them written
|
|
||||||
up elsewhere you can link to that doc here.
|
|
||||||
|
|
||||||
All common PTL duties are enumerated in the `PTL guide
|
|
||||||
<https://docs.openstack.org/project-team-guide/ptl.html>`_.
|
|
@ -1,25 +0,0 @@
|
|||||||
.. oswin-tempest-plugin documentation master file, created by
|
|
||||||
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
Welcome to oswin-tempest-plugin's documentation!
|
|
||||||
================================================
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
readme
|
|
||||||
installation
|
|
||||||
usage
|
|
||||||
contributing
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
============
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
At the command line::
|
|
||||||
|
|
||||||
$ pip install oswin-tempest-plugin
|
|
||||||
|
|
||||||
Or, if you have virtualenvwrapper installed::
|
|
||||||
|
|
||||||
$ mkvirtualenv oswin-tempest-plugin
|
|
||||||
$ pip install oswin-tempest-plugin
|
|
@ -1 +0,0 @@
|
|||||||
.. include:: ../../README.rst
|
|
@ -1,7 +0,0 @@
|
|||||||
=====
|
|
||||||
Usage
|
|
||||||
=====
|
|
||||||
|
|
||||||
To use oswin-tempest-plugin in a project::
|
|
||||||
|
|
||||||
import oswin_tempest_plugin
|
|
@ -1,90 +0,0 @@
|
|||||||
# Copyright 2013 Cloudbase Solutions Srl
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from winrm import protocol
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin import exceptions
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
protocol.Protocol.DEFAULT_TIMEOUT = "PT3600S"
|
|
||||||
|
|
||||||
|
|
||||||
def run_wsman_cmd(host, cmd, username, password=None,
|
|
||||||
cert_pem_path=None, cert_key_pem_path=None,
|
|
||||||
transport_method='plaintext', fail_on_error=True):
|
|
||||||
url = 'https://%s:5986/wsman' % host
|
|
||||||
|
|
||||||
if transport_method == 'ssl':
|
|
||||||
if not (os.path.exists(cert_pem_path)
|
|
||||||
and os.path.exists(cert_key_pem_path)):
|
|
||||||
raise exceptions.WSManException('Could not find certificate path '
|
|
||||||
'or certificate key path.')
|
|
||||||
|
|
||||||
LOG.debug('Connecting to: %s', host)
|
|
||||||
p = protocol.Protocol(endpoint=url,
|
|
||||||
transport=transport_method,
|
|
||||||
server_cert_validation='ignore',
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
cert_pem=cert_pem_path,
|
|
||||||
cert_key_pem=cert_key_pem_path)
|
|
||||||
|
|
||||||
shell_id = p.open_shell()
|
|
||||||
LOG.debug('Running command on host %(host)s: %(cmd)s',
|
|
||||||
{'host': host, 'cmd': cmd})
|
|
||||||
command_id = p.run_command(shell_id, cmd)
|
|
||||||
std_out, std_err, return_code = p.get_command_output(shell_id, command_id)
|
|
||||||
|
|
||||||
p.cleanup_command(shell_id, command_id)
|
|
||||||
p.close_shell(shell_id)
|
|
||||||
|
|
||||||
LOG.debug('Results from %(host)s: return_code: %(return_code)s, std_out: '
|
|
||||||
'%(std_out)s, std_err: %(std_err)s',
|
|
||||||
{'host': host, 'return_code': return_code, 'std_out': std_out,
|
|
||||||
'std_err': std_err})
|
|
||||||
|
|
||||||
if fail_on_error and return_code:
|
|
||||||
raise exceptions.WSManException(
|
|
||||||
cmd=cmd, host=host, return_code=return_code,
|
|
||||||
std_out=std_out, std_err=std_err)
|
|
||||||
|
|
||||||
return (std_out, std_err, return_code)
|
|
||||||
|
|
||||||
|
|
||||||
def run_wsman_ps(host, cmd, username, password, cert_pem_path=None,
|
|
||||||
cert_key_pem_path=None, transport_method='plaintext',
|
|
||||||
fail_on_error=True):
|
|
||||||
|
|
||||||
cmd = ("powershell -NonInteractive -ExecutionPolicy RemoteSigned "
|
|
||||||
"-Command \"%s\"" % cmd)
|
|
||||||
return run_wsman_cmd(host, cmd, username, password, cert_pem_path,
|
|
||||||
cert_key_pem_path, transport_method, fail_on_error)
|
|
||||||
|
|
||||||
|
|
||||||
def run_hv_host_wsman_ps(host, cmd, fail_on_error=True):
|
|
||||||
return run_wsman_ps(
|
|
||||||
host, cmd,
|
|
||||||
username=CONF.hyperv_host_auth.username,
|
|
||||||
password=CONF.hyperv_host_auth.password,
|
|
||||||
cert_pem_path=CONF.hyperv_host_auth.cert_pem_path,
|
|
||||||
cert_key_pem_path=CONF.hyperv_host_auth.cert_key_pem_path,
|
|
||||||
transport_method=CONF.hyperv_host_auth.transport_method,
|
|
||||||
fail_on_error=fail_on_error)
|
|
@ -1,117 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_config import types
|
|
||||||
from tempest import config
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
hyperv_group = cfg.OptGroup(name='hyperv',
|
|
||||||
title='Hyper-V Driver Tempest Options')
|
|
||||||
|
|
||||||
HyperVGroup = [
|
|
||||||
cfg.IntOpt('hypervisor_version',
|
|
||||||
default=0,
|
|
||||||
help="Compute nodes' hypervisor version, used to determine "
|
|
||||||
"which tests to run. It must have following value: "
|
|
||||||
"major_version * 1000 + minor_version"
|
|
||||||
"For example, Windows / Hyper-V Server 2012 R2 would have "
|
|
||||||
"the value 6003"),
|
|
||||||
cfg.StrOpt('vhd_image_ref',
|
|
||||||
help="Valid VHD image reference to be used in tests."),
|
|
||||||
cfg.StrOpt('vhdx_image_ref',
|
|
||||||
help="Valid VHDX image reference to be used in tests."),
|
|
||||||
cfg.StrOpt('gen2_image_ref',
|
|
||||||
help="Valid Generation 2 VM VHDX image reference to be used "
|
|
||||||
"in tests."),
|
|
||||||
cfg.StrOpt('secure_boot_image_ref',
|
|
||||||
help="Valid secure boot VM VHDX image reference to be used "
|
|
||||||
"in tests."),
|
|
||||||
cfg.StrOpt('secure_boot_image_ssh_user',
|
|
||||||
help='User for secure boot image to be used in tests.'),
|
|
||||||
cfg.BoolOpt('cluster_enabled',
|
|
||||||
default=False,
|
|
||||||
help="The compute nodes are joined into a Hyper-V Cluster."),
|
|
||||||
cfg.IntOpt('failover_timeout',
|
|
||||||
default=120,
|
|
||||||
help='The maximum amount of time to wait for a failover to '
|
|
||||||
'occur.'),
|
|
||||||
cfg.IntOpt('failover_sleep_interval',
|
|
||||||
default=5,
|
|
||||||
help='The amount of time to wait between failover checks.'),
|
|
||||||
cfg.BoolOpt('remotefx_enabled',
|
|
||||||
default=False,
|
|
||||||
help="RemoteFX feature is enabled and supported on the "
|
|
||||||
"compute nodes."),
|
|
||||||
cfg.IntOpt('available_numa_nodes',
|
|
||||||
default=1,
|
|
||||||
help="The maximum number of NUMA cells the compute nodes "
|
|
||||||
"have. If it's less than 2, resize negative tests for "
|
|
||||||
"vNUMA will be skipped."),
|
|
||||||
cfg.ListOpt('collected_metrics',
|
|
||||||
item_type=types.String(
|
|
||||||
choices=('cpu', 'network.outgoing.bytes',
|
|
||||||
'disk.read.bytes')),
|
|
||||||
default=[],
|
|
||||||
help="The ceilometer metrics to check. If this config value "
|
|
||||||
"is empty, the telemetry tests are skipped. This config "
|
|
||||||
"option assumes that the compute nodes are configured "
|
|
||||||
"and capable of collecting ceilometer metrics. WARNING: "
|
|
||||||
"neutron-ovs-agent is not capable of enabling network "
|
|
||||||
"metrics collection."),
|
|
||||||
cfg.IntOpt('polled_metrics_delay',
|
|
||||||
default=620,
|
|
||||||
help="The number of seconds to wait for the metrics to be "
|
|
||||||
"published by the compute node's ceilometer-polling "
|
|
||||||
"agent. The value must be greater by ~15-20 seconds "
|
|
||||||
"than the agent's publish interval, defined in its "
|
|
||||||
"polling.yaml file (typically, the intervals are 600 "
|
|
||||||
"seconds)."),
|
|
||||||
]
|
|
||||||
|
|
||||||
hyperv_host_auth_group = cfg.OptGroup(name='hyperv_host_auth',
|
|
||||||
title='Hyper-V host '
|
|
||||||
'authentication options')
|
|
||||||
|
|
||||||
hyperv_host_auth_opts = [
|
|
||||||
cfg.StrOpt('username',
|
|
||||||
help="The username of the Hyper-V hosts."),
|
|
||||||
cfg.StrOpt('password',
|
|
||||||
secret=True,
|
|
||||||
help='The password of the Hyper-V hosts.'),
|
|
||||||
cfg.StrOpt('cert_pem_path',
|
|
||||||
default=None,
|
|
||||||
help='SSL certificate for WinRM remote PS connection.'),
|
|
||||||
cfg.StrOpt('cert_key_pem_path',
|
|
||||||
default=None,
|
|
||||||
help='SSL key paired with cert_pem_path for WinRM remote PS '
|
|
||||||
'connection.'),
|
|
||||||
cfg.StrOpt('transport_method',
|
|
||||||
default='plaintext',
|
|
||||||
choices=('ssl', 'ntlm', 'plaintext'),
|
|
||||||
help='The method that should be used to establish a connection '
|
|
||||||
'to a Hyper-V host.')
|
|
||||||
]
|
|
||||||
|
|
||||||
_opts = [
|
|
||||||
(hyperv_group, HyperVGroup),
|
|
||||||
(hyperv_host_auth_group, hyperv_host_auth_opts),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
|
||||||
return _opts
|
|
@ -1,31 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
|
|
||||||
|
|
||||||
class ResizeException(exceptions.TempestException):
|
|
||||||
message = ("Server %(server_id)s failed to resize to the given "
|
|
||||||
"flavor %(flavor)s")
|
|
||||||
|
|
||||||
|
|
||||||
class NotFoundException(exceptions.TempestException):
|
|
||||||
message = "Resource %(resource)s (%(res_type)s) was not found."
|
|
||||||
|
|
||||||
|
|
||||||
class WSManException(exceptions.TempestException):
|
|
||||||
message = ('Command "%(cmd)s" failed on host %(host)s failed with the '
|
|
||||||
'return code %(return_code)s. std_out: %(std_out)s, '
|
|
||||||
'std_err: %(std_err)s')
|
|
@ -1,65 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from tempest import config
|
|
||||||
from tempest.test_discover import plugins
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config as project_config
|
|
||||||
|
|
||||||
|
|
||||||
class OSWinTempestPlugin(plugins.TempestPlugin):
|
|
||||||
def load_tests(self):
|
|
||||||
base_path = os.path.split(os.path.dirname(
|
|
||||||
os.path.abspath(__file__)))[0]
|
|
||||||
test_dir = "oswin_tempest_plugin/tests"
|
|
||||||
full_test_dir = os.path.join(base_path, test_dir)
|
|
||||||
return full_test_dir, base_path
|
|
||||||
|
|
||||||
def register_opts(self, conf):
|
|
||||||
"""Add additional configuration options to tempest.
|
|
||||||
|
|
||||||
This method will be run for the plugin during the register_opts()
|
|
||||||
function in tempest.config
|
|
||||||
|
|
||||||
:param conf: The conf object that can be used to register additional
|
|
||||||
config options on.
|
|
||||||
"""
|
|
||||||
|
|
||||||
for config_opt_group, config_opts in project_config.list_opts():
|
|
||||||
config.register_opt_group(conf, config_opt_group, config_opts)
|
|
||||||
|
|
||||||
def get_opt_lists(self):
|
|
||||||
"""Get a list of options for sample config generation.
|
|
||||||
|
|
||||||
:return: A list of tuples with the group name and options in that
|
|
||||||
group.
|
|
||||||
:return type: list
|
|
||||||
"""
|
|
||||||
return [(group.name, opts)
|
|
||||||
for group, opts in project_config.list_opts()]
|
|
||||||
|
|
||||||
def get_service_clients(self):
|
|
||||||
metric_config = config.service_client_config('metric')
|
|
||||||
metric_v1_params = {
|
|
||||||
'name': 'metric_v1',
|
|
||||||
'service_version': 'metric.v1',
|
|
||||||
'module_path': 'oswin_tempest_plugin.services.gnocchi_client',
|
|
||||||
'client_names': ['GnocchiClient'],
|
|
||||||
}
|
|
||||||
metric_v1_params.update(metric_config)
|
|
||||||
|
|
||||||
return [metric_v1_params]
|
|
@ -1,50 +0,0 @@
|
|||||||
# Copyright 2018 Cloudbase Solutions Srl
|
|
||||||
#
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oslo_serialization import jsonutils as json
|
|
||||||
from tempest.lib.common import rest_client
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class GnocchiClient(rest_client.RestClient):
|
|
||||||
|
|
||||||
uri_prefix = 'v1'
|
|
||||||
|
|
||||||
def deserialize(self, body):
|
|
||||||
return json.loads(body.replace("\n", ""))
|
|
||||||
|
|
||||||
def _helper_list(self, uri):
|
|
||||||
resp, body = self.get(uri)
|
|
||||||
self.expected_success(200, resp.status)
|
|
||||||
body = self.deserialize(body)
|
|
||||||
return rest_client.ResponseBodyList(resp, body)
|
|
||||||
|
|
||||||
def list_resources(self):
|
|
||||||
uri = '%s/resource/generic' % self.uri_prefix
|
|
||||||
return self._helper_list(uri)
|
|
||||||
|
|
||||||
def list_samples(self, resource_id, meter_name):
|
|
||||||
"""Returns a list of samples for the given resource and meter.
|
|
||||||
|
|
||||||
:returns: list, each item being a list containing the following values
|
|
||||||
in this order: timestamp, granularity, value.
|
|
||||||
"""
|
|
||||||
uri = '%s/resource/generic/%s/metric/%s/measures' % (
|
|
||||||
self.uri_prefix, resource_id, meter_name)
|
|
||||||
return self._helper_list(uri)
|
|
@ -1,107 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from tempest.common import waiters
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class _MigrateMixin(object):
|
|
||||||
"""Cold migration mixin.
|
|
||||||
|
|
||||||
This mixin will add a cold migration test. It will perform the
|
|
||||||
following operations:
|
|
||||||
|
|
||||||
* Spawn instance.
|
|
||||||
* Cold migrate the instance.
|
|
||||||
* Check the server connectivity.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _migrate_server(self, server_tuple):
|
|
||||||
server = server_tuple.server
|
|
||||||
self.admin_servers_client.migrate_server(server['id'])
|
|
||||||
self._wait_for_server_status(server, 'VERIFY_RESIZE')
|
|
||||||
self.servers_client.confirm_resize_server(server['id'])
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute.min_compute_nodes >= 2,
|
|
||||||
'Expected at least 2 compute nodes.')
|
|
||||||
def test_migration(self):
|
|
||||||
server_tuple = self._create_server()
|
|
||||||
self._migrate_server(server_tuple)
|
|
||||||
self._check_scenario(server_tuple)
|
|
||||||
|
|
||||||
|
|
||||||
class _LiveMigrateMixin(object):
|
|
||||||
"""Live migration mixin.
|
|
||||||
|
|
||||||
This mixin will add a live migration test. It will perform the
|
|
||||||
following operations:
|
|
||||||
|
|
||||||
* Spawn instance.
|
|
||||||
* Live migrate the instance.
|
|
||||||
* Check the server connectivity.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO(amuresan): Different mixins may be used at the same time.
|
|
||||||
# Each of them may override some fields such as
|
|
||||||
# 'max_microversion'. This has to be sorted out.
|
|
||||||
max_microversion = '2.24'
|
|
||||||
|
|
||||||
def _live_migrate_server(self, server_tuple, destination_host=None,
|
|
||||||
state='ACTIVE', volume_backed=False):
|
|
||||||
server = server_tuple.server
|
|
||||||
admin_server = self._get_server_as_admin(server)
|
|
||||||
current_host = admin_server['OS-EXT-SRV-ATTR:host']
|
|
||||||
|
|
||||||
block_migration = (CONF.compute_feature_enabled.
|
|
||||||
block_migration_for_live_migration
|
|
||||||
and not volume_backed)
|
|
||||||
|
|
||||||
self.admin_servers_client.live_migrate_server(
|
|
||||||
server['id'],
|
|
||||||
host=destination_host,
|
|
||||||
block_migration=block_migration,
|
|
||||||
disk_over_commit=False)
|
|
||||||
|
|
||||||
waiters.wait_for_server_status(self.admin_servers_client, server['id'],
|
|
||||||
state)
|
|
||||||
|
|
||||||
admin_server = self._get_server_as_admin(server)
|
|
||||||
after_migration_host = admin_server['OS-EXT-SRV-ATTR:host']
|
|
||||||
|
|
||||||
migration_list = (self.admin_migrations_client.list_migrations()
|
|
||||||
['migrations'])
|
|
||||||
|
|
||||||
msg = ("Live Migration failed. Migrations list for Instance "
|
|
||||||
"%s: [" % server['id'])
|
|
||||||
for live_migration in migration_list:
|
|
||||||
if live_migration['instance_uuid'] == server['id']:
|
|
||||||
msg += "\n%s" % live_migration
|
|
||||||
msg += "]"
|
|
||||||
|
|
||||||
if destination_host:
|
|
||||||
self.assertEqual(destination_host, after_migration_host, msg)
|
|
||||||
else:
|
|
||||||
self.assertNotEqual(current_host, after_migration_host, msg)
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.live_migration,
|
|
||||||
'Live migration option enabled.')
|
|
||||||
def test_live_migration(self):
|
|
||||||
server_tuple = self._create_server()
|
|
||||||
self._live_migrate_server(server_tuple)
|
|
||||||
self._check_scenario(server_tuple)
|
|
@ -1,86 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin.tests._mixins import resize
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class _OptionalFeatureMixin(resize._ResizeUtils):
|
|
||||||
"""Optional Feature mixin.
|
|
||||||
|
|
||||||
Some features are optional, and can be turned on / off through resize
|
|
||||||
with certain flavors.
|
|
||||||
Examples of optional features: vNUMA, QoS, SR-IOV, PCI passthrough,
|
|
||||||
RemoteFX.
|
|
||||||
|
|
||||||
This mixin will add the following tests:
|
|
||||||
* test_feature
|
|
||||||
* test_resize_add_feature
|
|
||||||
* test_resize_remove_feature
|
|
||||||
|
|
||||||
The resize tests will create a new instance, resize it to a new flavor (
|
|
||||||
turning on / off the optional feature), and check its network
|
|
||||||
connectivity.
|
|
||||||
|
|
||||||
The optional feature flavor is based on the test suite's configured
|
|
||||||
_FLAVOR_REF (typically compute.flavor_ref or compute.flavor_ref_alt),
|
|
||||||
with some updates. For example, if the vNUMA configuration is to be tested,
|
|
||||||
the new flavor would contain the flavor extra_spec {'hw:numa_nodes="1"'}.
|
|
||||||
Keep in mind that all the extra_spec keys and values have to be strings.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# NOTE(claudiub): This flavor dict contains updates to the base flavor
|
|
||||||
# tempest is configured with. For example, _FEATURE_FLAVOR can be:
|
|
||||||
# _BIGGER_FLAVOR = {'extra_specs': {'hw:numa_nodes': 1'}}
|
|
||||||
# which means a flavor having that flavor extra_spec will be created, and
|
|
||||||
# a created instance will be resize to / from it.
|
|
||||||
|
|
||||||
_FEATURE_FLAVOR = {}
|
|
||||||
|
|
||||||
def _get_flavor_ref(self):
|
|
||||||
"""Gets a new optional feature flavor ref.
|
|
||||||
|
|
||||||
Creates a new flavor based on the test suite's configured _FLAVOR_REF,
|
|
||||||
with some updates specific to the optional feature.
|
|
||||||
|
|
||||||
:returns: nova flavor ID.
|
|
||||||
"""
|
|
||||||
# NOTE(claudiub): Unless explicitly given another flavor,
|
|
||||||
# _create_server will call this method to get the flavor reference
|
|
||||||
# needed to spawn a new instance. Thus, any other test will spawn
|
|
||||||
# instances with this Optional Feature.
|
|
||||||
new_flavor = self._create_new_flavor(self._FLAVOR_REF,
|
|
||||||
self._FEATURE_FLAVOR)
|
|
||||||
return new_flavor['id']
|
|
||||||
|
|
||||||
def test_feature(self):
|
|
||||||
server_tuple = self._create_server()
|
|
||||||
self._check_scenario(server_tuple)
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
|
||||||
'Resize is not available.')
|
|
||||||
def test_resize_add_feature(self):
|
|
||||||
new_flavor = self._get_flavor_ref()
|
|
||||||
self._check_resize(new_flavor, self._FLAVOR_REF)
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
|
||||||
'Resize is not available.')
|
|
||||||
def test_resize_remove_feature(self):
|
|
||||||
new_flavor = self._get_flavor_ref()
|
|
||||||
self._check_resize(self._FLAVOR_REF, new_flavor)
|
|
@ -1,127 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin import exceptions
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class _ResizeUtils(object):
|
|
||||||
|
|
||||||
def _get_server_migration(self, server_id):
|
|
||||||
final_states = ['error', 'confirmed']
|
|
||||||
for i in range(10):
|
|
||||||
migrations = (
|
|
||||||
self.admin_migrations_client.list_migrations()['migrations'])
|
|
||||||
server_migration = [m for m in migrations
|
|
||||||
if m['instance_uuid'] == server_id]
|
|
||||||
if server_migration:
|
|
||||||
migration_status = server_migration[0]['status']
|
|
||||||
LOG.debug("Server's %s migration status: %s",
|
|
||||||
server_id, migration_status)
|
|
||||||
if migration_status in final_states:
|
|
||||||
return server_migration[0]
|
|
||||||
else:
|
|
||||||
# NOTE(claudiub): the migration might not appear *immediately*
|
|
||||||
# after the cold resize was requested.
|
|
||||||
LOG.info("Server's %s migration was not found.", server_id)
|
|
||||||
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
return server_migration[0] if server_migration else None
|
|
||||||
|
|
||||||
def _resize_server(self, server_tuple, new_flavor_id):
|
|
||||||
server = server_tuple.server
|
|
||||||
self.servers_client.resize_server(server['id'],
|
|
||||||
flavor_ref=new_flavor_id)
|
|
||||||
|
|
||||||
migration = self._get_server_migration(server['id'])
|
|
||||||
if migration and migration['status'] == 'error':
|
|
||||||
# the migration ended up in an error state. Raise an exception.
|
|
||||||
raise exceptions.ResizeException(server_id=server['id'],
|
|
||||||
flavor=new_flavor_id)
|
|
||||||
|
|
||||||
self._wait_for_server_status(server, 'VERIFY_RESIZE')
|
|
||||||
self.servers_client.confirm_resize_server(server['id'])
|
|
||||||
|
|
||||||
def _check_resize(self, resize_flavor_id, original_flavor_id=None,
|
|
||||||
expected_fail=False):
|
|
||||||
original_flavor_id = original_flavor_id or self._get_flavor_ref()
|
|
||||||
server_tuple = self._create_server(original_flavor_id)
|
|
||||||
|
|
||||||
if expected_fail:
|
|
||||||
self.assertRaises(exceptions.ResizeException,
|
|
||||||
self._resize_server,
|
|
||||||
server_tuple, resize_flavor_id)
|
|
||||||
else:
|
|
||||||
self._resize_server(server_tuple, resize_flavor_id)
|
|
||||||
|
|
||||||
# assert that the server is still reachable, even if the resize
|
|
||||||
# failed.
|
|
||||||
self._check_scenario(server_tuple)
|
|
||||||
|
|
||||||
|
|
||||||
class _ResizeMixin(_ResizeUtils):
|
|
||||||
"""Cold resize mixin.
|
|
||||||
|
|
||||||
This mixin will add cold resize tests. The tests will create a new
|
|
||||||
instance, resize it to a new flavor, and check its network connectivity.
|
|
||||||
|
|
||||||
The new flavor is based on the test suite's configured _FLAVOR_REF (
|
|
||||||
typically compute.flavor_ref or compute.flavor_ref_alt), with some
|
|
||||||
updates. For example, if the vNUMA configuration is to be tested, the new
|
|
||||||
flavor would contain the flavor extra_spec 'hw:numa_nodes=1'.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# NOTE(claudiub): These flavor dicts are updates to the base flavor
|
|
||||||
# tempest is configured with. For example, _BIGGER_FLAVOR can be:
|
|
||||||
# _BIGGER_FLAVOR = {'disk': 1}
|
|
||||||
# which means a flavor having +1 GB disk size will be created, and
|
|
||||||
# a created instance will be resized to it.
|
|
||||||
|
|
||||||
_SMALLER_FLAVOR = {}
|
|
||||||
_BIGGER_FLAVOR = {}
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
|
||||||
'Resize is not available.')
|
|
||||||
def test_resize(self):
|
|
||||||
new_flavor = self._create_new_flavor(self._get_flavor_ref(),
|
|
||||||
self._BIGGER_FLAVOR)
|
|
||||||
self._check_resize(new_flavor['id'])
|
|
||||||
|
|
||||||
|
|
||||||
class _ResizeNegativeMixin(_ResizeUtils):
|
|
||||||
"""Cold resize negative mixin.
|
|
||||||
|
|
||||||
This mixin will add cold resize negative tests. The tests will create a
|
|
||||||
new instance, resize it to an invalid flavor, check that the resize
|
|
||||||
failed, and check the instance's connectivity.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_BAD_FLAVOR = {}
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
|
||||||
'Resize is not available.')
|
|
||||||
def test_resize_negative(self):
|
|
||||||
new_flavor = self._create_new_flavor(self._get_flavor_ref(),
|
|
||||||
self._BAD_FLAVOR)
|
|
||||||
self._check_resize(new_flavor['id'], expected_fail=True)
|
|
@ -1,153 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from tempest.lib import exceptions as lib_exc
|
|
||||||
|
|
||||||
from oswin_tempest_plugin.clients import wsman
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin import exceptions
|
|
||||||
from oswin_tempest_plugin.tests._mixins import migrate
|
|
||||||
from oswin_tempest_plugin.tests._mixins import resize
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class HyperVClusterTest(migrate._MigrateMixin,
|
|
||||||
migrate._LiveMigrateMixin,
|
|
||||||
resize._ResizeMixin,
|
|
||||||
test_base.TestBase):
|
|
||||||
|
|
||||||
"""The test suite for the Hyper-V Cluster.
|
|
||||||
|
|
||||||
This test suite will test the functionality of the Hyper-V Cluster Driver
|
|
||||||
in OpenStack. The tests will force a failover on its newly created
|
|
||||||
instance, and asserts the following:
|
|
||||||
|
|
||||||
* the instance moves to another host.
|
|
||||||
* the nova instance's host is properly updated.
|
|
||||||
* the instance's network connection still works.
|
|
||||||
* different nova operations can be performed properly.
|
|
||||||
|
|
||||||
This test suite relies on the fact that there are at least 2 compute nodes
|
|
||||||
available, that they are clustered, and have WSMan configured.
|
|
||||||
|
|
||||||
The test suite contains the following tests:
|
|
||||||
|
|
||||||
* test_check_clustered_vm
|
|
||||||
* test_check_migration
|
|
||||||
* test_check_resize
|
|
||||||
* test_check_resize_negative
|
|
||||||
"""
|
|
||||||
|
|
||||||
_BIGGER_FLAVOR = {'disk': 1}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(HyperVClusterTest, cls).skip_checks()
|
|
||||||
|
|
||||||
# check if the cluster Tests can be run.
|
|
||||||
if not CONF.hyperv.cluster_enabled:
|
|
||||||
msg = 'Hyper-V cluster tests are disabled.'
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
|
|
||||||
if not CONF.hyperv_host_auth.username:
|
|
||||||
msg = ('No Hyper-V host username has been provided. '
|
|
||||||
'Skipping cluster tests.')
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
|
|
||||||
if not CONF.compute.min_compute_nodes >= 2:
|
|
||||||
msg = 'Expected at least 2 compute nodes.'
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
|
|
||||||
def _failover_server(self, server_name, host_ip):
|
|
||||||
"""Triggers the failover for the given server on the given host."""
|
|
||||||
|
|
||||||
resource_name = "Virtual Machine %s" % server_name
|
|
||||||
cmd = "Test-ClusterResourceFailure -Name '%s'" % resource_name
|
|
||||||
# NOTE(claudiub): we issue the failover command twice, because on
|
|
||||||
# the first failure, the Hyper-V Cluster will prefer the current
|
|
||||||
# node, and will try to reactivate the VM on the it, and it will
|
|
||||||
# succeed. On the 2nd failure, the VM will failover to another
|
|
||||||
# node. Also, there needs to be a delay between commands, so the
|
|
||||||
# original failover has time to finish.
|
|
||||||
wsman.run_hv_host_wsman_ps(host_ip, cmd)
|
|
||||||
time.sleep(CONF.hyperv.failover_sleep_interval)
|
|
||||||
wsman.run_hv_host_wsman_ps(host_ip, cmd)
|
|
||||||
|
|
||||||
def _wait_for_failover(self, server, original_host):
|
|
||||||
"""Waits for the given server to failover to another host.
|
|
||||||
|
|
||||||
:raises TimeoutException: if the given server did not failover to
|
|
||||||
another host within the configured "CONF.hyperv.failover_timeout"
|
|
||||||
interval.
|
|
||||||
"""
|
|
||||||
LOG.debug('Waiting for server %(server)s to failover from '
|
|
||||||
'compute node %(host)s',
|
|
||||||
dict(server=server['id'], host=original_host))
|
|
||||||
|
|
||||||
start_time = int(time.time())
|
|
||||||
timeout = CONF.hyperv.failover_timeout
|
|
||||||
while True:
|
|
||||||
elapsed_time = int(time.time()) - start_time
|
|
||||||
admin_server = self._get_server_as_admin(server)
|
|
||||||
current_host = admin_server['OS-EXT-SRV-ATTR:host']
|
|
||||||
if current_host != original_host:
|
|
||||||
LOG.debug('Server %(server)s failovered from compute node '
|
|
||||||
'%(host)s in %(seconds)s seconds.',
|
|
||||||
dict(server=server['id'], host=original_host,
|
|
||||||
seconds=elapsed_time))
|
|
||||||
return
|
|
||||||
|
|
||||||
if elapsed_time >= timeout:
|
|
||||||
msg = ('Server %(server)s did not failover in the given '
|
|
||||||
'amount of time (%(timeout)s s).')
|
|
||||||
raise lib_exc.TimeoutException(
|
|
||||||
msg % dict(server=server['id'], timeout=timeout))
|
|
||||||
|
|
||||||
time.sleep(CONF.hyperv.failover_sleep_interval)
|
|
||||||
|
|
||||||
def _get_hypervisor(self, hostname):
|
|
||||||
hypervisors = self.admin_hypervisor_client.list_hypervisors(
|
|
||||||
detail=True)['hypervisors']
|
|
||||||
hypervisor = [h for h in hypervisors if
|
|
||||||
h['hypervisor_hostname'] == hostname]
|
|
||||||
|
|
||||||
if not hypervisor:
|
|
||||||
raise exceptions.NotFoundException(resource=hostname,
|
|
||||||
res_type='hypervisor')
|
|
||||||
return hypervisor[0]
|
|
||||||
|
|
||||||
def _create_server(self, flavor=None):
|
|
||||||
server_tuple = super(HyperVClusterTest, self)._create_server(flavor)
|
|
||||||
server = server_tuple.server
|
|
||||||
admin_server = self._get_server_as_admin(server)
|
|
||||||
|
|
||||||
server_name = admin_server['OS-EXT-SRV-ATTR:instance_name']
|
|
||||||
hostname = admin_server['OS-EXT-SRV-ATTR:host']
|
|
||||||
host_ip = self._get_hypervisor(hostname)['host_ip']
|
|
||||||
|
|
||||||
self._failover_server(server_name, host_ip)
|
|
||||||
self._wait_for_failover(server, hostname)
|
|
||||||
|
|
||||||
return server_tuple
|
|
||||||
|
|
||||||
def test_clustered_vm(self):
|
|
||||||
server_tuple = self._create_server()
|
|
||||||
self._check_scenario(server_tuple)
|
|
@ -1,91 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin.tests._mixins import migrate
|
|
||||||
from oswin_tempest_plugin.tests._mixins import resize
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class _BaseDiskTestMixin(migrate._MigrateMixin,
|
|
||||||
resize._ResizeMixin,
|
|
||||||
resize._ResizeNegativeMixin):
|
|
||||||
"""Image types / formats test suite.
|
|
||||||
|
|
||||||
This test suite will spawn instances with a configured image and will
|
|
||||||
check their network connectivity. The purpose of this test suite is to
|
|
||||||
cover different image formats and types (VHD, VHDX, Generation 2 VMs).
|
|
||||||
"""
|
|
||||||
|
|
||||||
_CONF_OPTION_NAME = ''
|
|
||||||
|
|
||||||
_BIGGER_FLAVOR = {'disk': 1}
|
|
||||||
_BAD_FLAVOR = {'disk': -1}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(_BaseDiskTestMixin, cls).skip_checks()
|
|
||||||
# check if the needed image ref has been configured.
|
|
||||||
if not cls._IMAGE_REF:
|
|
||||||
msg = ('The config option "%s" has not been set. Skipping.' %
|
|
||||||
cls._CONF_OPTION_NAME)
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
|
|
||||||
def test_disk(self):
|
|
||||||
server_tuple = self._create_server()
|
|
||||||
self._check_scenario(server_tuple)
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
|
||||||
'Resize is not available.')
|
|
||||||
def test_resize_negative(self):
|
|
||||||
# NOTE(claudiub): This test will try to downsize a VM's disk, which is
|
|
||||||
# unsupported. The configured flavor might have disk set to 1GB.
|
|
||||||
# The nova-api does not allow disks to be resized on 0 GB.
|
|
||||||
flavor = self._get_flavor_ref()
|
|
||||||
new_flavor = self._create_new_flavor(flavor, self._BIGGER_FLAVOR)
|
|
||||||
self._check_resize(flavor, new_flavor['id'], expected_fail=True)
|
|
||||||
|
|
||||||
|
|
||||||
class VhdDiskTest(_BaseDiskTestMixin, test_base.TestBase):
|
|
||||||
|
|
||||||
_IMAGE_REF = CONF.hyperv.vhd_image_ref
|
|
||||||
_CONF_OPTION_NAME = 'hyperv.vhd_image_ref'
|
|
||||||
_FLAVOR_SUFFIX = 'vhd'
|
|
||||||
|
|
||||||
# TODO(claudiub): validate that the images really are VHD / VHDX.
|
|
||||||
|
|
||||||
|
|
||||||
class VhdxDiskTest(_BaseDiskTestMixin, test_base.TestBase):
|
|
||||||
|
|
||||||
_IMAGE_REF = CONF.hyperv.vhdx_image_ref
|
|
||||||
_CONF_OPTION_NAME = 'hyperv.vhdx_image_ref'
|
|
||||||
_FLAVOR_SUFFIX = 'vhdx'
|
|
||||||
|
|
||||||
|
|
||||||
class Generation2DiskTest(_BaseDiskTestMixin, test_base.TestBase):
|
|
||||||
|
|
||||||
# Generation 2 VMs have been introduced in Windows / Hyper-V Server 2012 R2
|
|
||||||
_MIN_HYPERV_VERSION = 6003
|
|
||||||
|
|
||||||
_IMAGE_REF = CONF.hyperv.gen2_image_ref
|
|
||||||
_CONF_OPTION_NAME = 'hyperv.gen2_image_ref'
|
|
||||||
_FLAVOR_SUFFIX = 'gen2'
|
|
||||||
|
|
||||||
# TODO(claudiub): Add validation that the given gen2_image_ref really has
|
|
||||||
# the 'hw_machine_type=hyperv-gen2' property.
|
|
@ -1,166 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from tempest import clients
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class ClientManager(clients.Manager):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(ClientManager, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.set_gnocchi_client()
|
|
||||||
|
|
||||||
def set_gnocchi_client(self):
|
|
||||||
self.gnocchi_client = self.metric_v1.GnocchiClient()
|
|
||||||
|
|
||||||
|
|
||||||
class MetricsCollectionTestCase(test_base.TestBase):
|
|
||||||
"""Adds metrics collection scenario tests.
|
|
||||||
|
|
||||||
This test suite verifies that the instance metrics are properly published
|
|
||||||
and collected and have non-zero values. The verification is done via the
|
|
||||||
ceilometer API.
|
|
||||||
|
|
||||||
setup:
|
|
||||||
1. spins a new instance.
|
|
||||||
2. waits until the instance was created succesfully (ACTIVE status).
|
|
||||||
3. wait an interval of time which represents the polling period of the
|
|
||||||
ceilometer-polling agent.
|
|
||||||
|
|
||||||
Waiting for the ceilometer-polling agent to poll the resources is crucial,
|
|
||||||
otherwise the test suite will fail due to the fact that no samples
|
|
||||||
would be found published before checking the samples.
|
|
||||||
|
|
||||||
The test suite's polled_metrics_delay must have a greater value than the
|
|
||||||
ceilometer agent's polling interval. This can be done in two ways:
|
|
||||||
a. Configure tempest's polled_metric_delay, by adding the
|
|
||||||
following line in tempest.conf, in the hyperv section:
|
|
||||||
polled_metrics_delay = <desired value>
|
|
||||||
b. Set the interval value in polling.yaml on the compute node to
|
|
||||||
the desired value and restart the ceilometer polling agent. The
|
|
||||||
interval value is set either for the 'meter_source' or for each
|
|
||||||
of the following: 'cpu_source', 'disk_source', 'network_source'.
|
|
||||||
|
|
||||||
Note: If the polled_metrics_delay value is too low, the tests might not
|
|
||||||
find any samples and fail because of this. As a recommandation,
|
|
||||||
polled_metrics_delay's value should be:
|
|
||||||
polled_metric_delay = <polling.yaml interval value> + <15-20 seconds>
|
|
||||||
|
|
||||||
tests:
|
|
||||||
1. test_metrics - tests values for the following metrics:
|
|
||||||
- cpu
|
|
||||||
- network.outgoing.bytes
|
|
||||||
- disk.read.bytes
|
|
||||||
|
|
||||||
assumptions:
|
|
||||||
1. Ceilometer agent on the compute node is running.
|
|
||||||
2. Ceilometer agent on the compute node has the polling interval
|
|
||||||
defined in polling.yaml lower than the polled_metrics_delay defined
|
|
||||||
in this test suite.
|
|
||||||
3. The compute nodes' nova-compute and neutron-hyperv-agent services
|
|
||||||
have been configured to enable metrics collection.
|
|
||||||
"""
|
|
||||||
|
|
||||||
client_manager = ClientManager
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(MetricsCollectionTestCase, cls).skip_checks()
|
|
||||||
|
|
||||||
for service in ['ceilometer', 'gnocchi']:
|
|
||||||
if not getattr(CONF.service_available, service):
|
|
||||||
raise cls.skipException("%s service is required." % service)
|
|
||||||
|
|
||||||
if not CONF.hyperv.collected_metrics:
|
|
||||||
raise cls.skipException("Collected metrics not configured.")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_clients(cls):
|
|
||||||
super(MetricsCollectionTestCase, cls).setup_clients()
|
|
||||||
|
|
||||||
# Telemetry client
|
|
||||||
cls.telemetry_client = cls.os_primary.gnocchi_client
|
|
||||||
|
|
||||||
def _check_samples(self, resource_id, meter_name):
|
|
||||||
LOG.info("Checking %(meter_name)s for resource %(resource_id)s" % {
|
|
||||||
'meter_name': meter_name, 'resource_id': resource_id})
|
|
||||||
|
|
||||||
samples = self.telemetry_client.list_samples(resource_id, meter_name)
|
|
||||||
self.assertNotEmpty(
|
|
||||||
samples,
|
|
||||||
'Client returned no samples for the given resource '
|
|
||||||
'"%(resource_id)s" and meter "%(meter_name)s".' % {
|
|
||||||
'resource_id': resource_id, 'meter_name': meter_name})
|
|
||||||
|
|
||||||
non_zero_valued_samples = [s for s in samples if s[2] > 0]
|
|
||||||
self.assertNotEmpty(
|
|
||||||
non_zero_valued_samples,
|
|
||||||
'All meter %(meter_name)s samples for resource '
|
|
||||||
'%(resource_id)s are 0.' % {'meter_name': meter_name,
|
|
||||||
'resource_id': resource_id})
|
|
||||||
|
|
||||||
def _get_instance_cpu_resource_id(self, server):
|
|
||||||
return server['id']
|
|
||||||
|
|
||||||
def _get_instance_disk_resource_id(self, server):
|
|
||||||
return server['id']
|
|
||||||
|
|
||||||
def _get_instance_port_resource_id(self, server):
|
|
||||||
# Note(claudiub): the format for the instance_port_resource_id is:
|
|
||||||
# %(OS-EXT-SRV-ATTR:instance_name)s-%(instance_id)s-%(port_id)s
|
|
||||||
# the instance returned by self.servers_client does not contain the
|
|
||||||
# OS-EXT-SRV-ATTR:instance_name field. Which means that the resource_id
|
|
||||||
# must be found in gnocchi's resources.
|
|
||||||
start_res_id = server['id']
|
|
||||||
resources = self.telemetry_client.list_resources()
|
|
||||||
res_ids = [r['id'] for r in resources
|
|
||||||
if r['original_resource_id'].startswith('instance-')
|
|
||||||
and start_res_id in r['original_resource_id']]
|
|
||||||
|
|
||||||
self.assertEqual(1, len(res_ids))
|
|
||||||
return res_ids[0]
|
|
||||||
|
|
||||||
def _check_scenario(self, server_tuple):
|
|
||||||
server = server_tuple.server
|
|
||||||
LOG.info("Waiting %s seconds for the ceilometer compute agents to "
|
|
||||||
"publish the samples.", CONF.hyperv.polled_metrics_delay)
|
|
||||||
time.sleep(CONF.hyperv.polled_metrics_delay)
|
|
||||||
|
|
||||||
# TODO(claudiub): Add more metrics.
|
|
||||||
if 'cpu' in CONF.hyperv.collected_metrics:
|
|
||||||
cpu_res_id = self._get_instance_cpu_resource_id(server)
|
|
||||||
self._check_samples(cpu_res_id, 'cpu')
|
|
||||||
|
|
||||||
if 'network.outgoing.bytes' in CONF.hyperv.collected_metrics:
|
|
||||||
port_res_id = self._get_instance_port_resource_id(server)
|
|
||||||
self._check_samples(port_res_id, 'network.outgoing.bytes')
|
|
||||||
|
|
||||||
if 'disk.read.bytes' in CONF.hyperv.collected_metrics:
|
|
||||||
disk_resource_id = self._get_instance_disk_resource_id(server)
|
|
||||||
self._check_samples(disk_resource_id, 'disk.read.bytes')
|
|
||||||
|
|
||||||
def test_metrics(self):
|
|
||||||
server_tuple = self._create_server()
|
|
||||||
self._check_scenario(server_tuple)
|
|
@ -1,37 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oslo_utils import units
|
|
||||||
|
|
||||||
from oswin_tempest_plugin.tests._mixins import optional_feature
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
|
|
||||||
class QosTestCase(optional_feature._OptionalFeatureMixin,
|
|
||||||
test_base.TestBase):
|
|
||||||
"""QoS test suite.
|
|
||||||
|
|
||||||
This test suite will spawn instances with QoS specs.
|
|
||||||
|
|
||||||
Hyper-V uses normalized IOPS (8 KB increments), and the minimum IOPS that
|
|
||||||
can be set is 1.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Hyper-V disk QoS is supported on Windows / Hyper-v Server 2012 R2
|
|
||||||
# or newer.
|
|
||||||
_MIN_HYPERV_VERSION = 6003
|
|
||||||
|
|
||||||
_FEATURE_FLAVOR = {
|
|
||||||
'extra_specs': {'quota:disk_total_bytes_sec': str(units.Mi)}}
|
|
@ -1,44 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin.tests._mixins import optional_feature
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteFxTestCase(optional_feature._OptionalFeatureMixin,
|
|
||||||
test_base.TestBase):
|
|
||||||
"""RemoteFX test suite.
|
|
||||||
|
|
||||||
This test suit will spawn instances with RemoteFX enabled.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# RemoteFX is supported in Windows / Hyper-V Server 2012 R2 and newer.
|
|
||||||
_MIN_HYPERV_VERSION = 6003
|
|
||||||
|
|
||||||
_FEATURE_FLAVOR = {'extra_specs': {'os_resolution': '1920x1200',
|
|
||||||
'os_monitors': '1',
|
|
||||||
'os_vram': '1024'}}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(RemoteFxTestCase, cls).skip_checks()
|
|
||||||
# the CONF.hyperv.remotefx_enabled config option needs to be enabled.
|
|
||||||
if not CONF.hyperv.remotefx_enabled:
|
|
||||||
msg = ('The config option "hyperv.remotefx_enabled" is False. '
|
|
||||||
'Skipping.')
|
|
||||||
raise cls.skipException(msg)
|
|
@ -1,70 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin.tests._mixins import optional_feature
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class SecureBootTestCase(optional_feature._OptionalFeatureMixin,
|
|
||||||
test_base.TestBase):
|
|
||||||
"""Secure boot test suite.
|
|
||||||
|
|
||||||
This test suite will spawn instances requiring secure boot to be
|
|
||||||
enabled.
|
|
||||||
|
|
||||||
This test suite will require a Generation 2 VHDX image, with a
|
|
||||||
Linux guest OS (it tests connectivity via SSH).
|
|
||||||
|
|
||||||
The configured image must contain the following properties:
|
|
||||||
* os_type=linux
|
|
||||||
* hw_machine_type=hyperv-gen2
|
|
||||||
|
|
||||||
Hyper-V Secure Boot was first introduced in Windows / Hyper-V Server 2012
|
|
||||||
R2, but support for Linux guests was introduced in Windows / Hyper-V
|
|
||||||
Server 2016, which is why this test suite will require compute nodes
|
|
||||||
with the OS version 10.0 or newer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_MIN_HYPERV_VERSION = 10000
|
|
||||||
|
|
||||||
# NOTE(amuresan):Images supporting secure boot usually require more disk
|
|
||||||
# space. We're trying to use the largest of the configured
|
|
||||||
# flavors.
|
|
||||||
|
|
||||||
_FLAVOR_REF = CONF.compute.flavor_ref_alt
|
|
||||||
_IMAGE_REF = CONF.hyperv.secure_boot_image_ref
|
|
||||||
_IMAGE_SSH_USER = CONF.hyperv.secure_boot_image_ssh_user
|
|
||||||
_FEATURE_FLAVOR = {'extra_specs': {'os:secure_boot': 'required'}}
|
|
||||||
|
|
||||||
# TODO(amuresan): the secure_boot_image_ref should be reused in
|
|
||||||
# more than one test case so we don't have to add a different
|
|
||||||
# image for every test.
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(SecureBootTestCase, cls).skip_checks()
|
|
||||||
# check if the needed image ref has been configured.
|
|
||||||
if not cls._IMAGE_REF:
|
|
||||||
msg = ('The config option "hyperv.secure_boot_image_ref" '
|
|
||||||
'has not been set. Skipping secure boot tests.')
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
|
|
||||||
if not cls._IMAGE_SSH_USER:
|
|
||||||
msg = ('The config option "hyperv.secure_boot_image_ssh_user" '
|
|
||||||
'has not been set. Skipping.')
|
|
||||||
raise cls.skipException(msg)
|
|
@ -1,61 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
from oswin_tempest_plugin.tests._mixins import migrate
|
|
||||||
from oswin_tempest_plugin.tests._mixins import optional_feature
|
|
||||||
from oswin_tempest_plugin.tests._mixins import resize
|
|
||||||
from oswin_tempest_plugin.tests import test_base
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class HyperVvNumaTestCase(migrate._MigrateMixin,
|
|
||||||
migrate._LiveMigrateMixin,
|
|
||||||
optional_feature._OptionalFeatureMixin,
|
|
||||||
resize._ResizeMixin,
|
|
||||||
resize._ResizeNegativeMixin,
|
|
||||||
test_base.TestBase):
|
|
||||||
"""Hyper-V vNUMA test suite.
|
|
||||||
|
|
||||||
This test suite will spawn instances requiring NUMA placement.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_FEATURE_FLAVOR = {'extra_specs': {'hw:numa_nodes': '1'}}
|
|
||||||
_BIGGER_FLAVOR = {'ram': 128, 'extra_specs': {'hw:numa_nodes': '1'}}
|
|
||||||
|
|
||||||
# NOTE(claudiub): Hyper-V does not support asymmetric NUMA topologies.
|
|
||||||
# The resize should fail in this case.
|
|
||||||
_BAD_FLAVOR = {'ram': 64, 'extra_specs': {
|
|
||||||
'hw:numa_nodes': '2', 'hw:numa_mem.0': '64', 'hw:numa_cpus.0': '0'}}
|
|
||||||
|
|
||||||
@testtools.skipUnless(CONF.hyperv.available_numa_nodes > 1,
|
|
||||||
'At least 2 NUMA nodes are required.')
|
|
||||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
|
||||||
'Resize is not available.')
|
|
||||||
def test_resize_negative(self):
|
|
||||||
new_flavor = self._create_new_flavor(self._get_flavor_ref(),
|
|
||||||
self._BAD_FLAVOR)
|
|
||||||
|
|
||||||
# NOTE(claudiub): all NUMA nodes have to be properly defined.
|
|
||||||
vcpus = [i for i in range(1, int(new_flavor['vcpus']))]
|
|
||||||
extra_specs = {'hw:numa_mem.1': str(int(new_flavor['ram']) - 64),
|
|
||||||
'hw:numa_cpus.1': ','.join(vcpus)}
|
|
||||||
self.admin_flavors_client.set_flavor_extra_spec(
|
|
||||||
new_flavor['id'], **extra_specs)
|
|
||||||
|
|
||||||
self._check_resize(new_flavor['id'], expected_fail=True)
|
|
@ -1,287 +0,0 @@
|
|||||||
# Copyright 2017 Cloudbase Solutions SRL
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import collections
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from tempest.common import compute
|
|
||||||
from tempest.common.utils.linux import remote_client
|
|
||||||
from tempest.common import waiters
|
|
||||||
from tempest.lib.common.utils import data_utils
|
|
||||||
from tempest.lib.common.utils import test_utils
|
|
||||||
from tempest.lib import exceptions
|
|
||||||
import tempest.scenario.manager
|
|
||||||
|
|
||||||
from oswin_tempest_plugin import config
|
|
||||||
|
|
||||||
CONF = config.CONF
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
Server_tuple = collections.namedtuple(
|
|
||||||
'Server_tuple',
|
|
||||||
['server', 'floating_ip', 'keypair', 'security_groups'])
|
|
||||||
|
|
||||||
|
|
||||||
class TestBase(tempest.scenario.manager.ScenarioTest):
|
|
||||||
"""Base class for tests."""
|
|
||||||
|
|
||||||
credentials = ['primary', 'admin']
|
|
||||||
|
|
||||||
# Inheriting TestCases should change this version if needed.
|
|
||||||
_MIN_HYPERV_VERSION = 6002
|
|
||||||
|
|
||||||
# Inheriting TestCases should change this image ref if needed.
|
|
||||||
_IMAGE_REF = CONF.compute.image_ref
|
|
||||||
|
|
||||||
# Inheriting TestCases should change this flavor ref if needed.
|
|
||||||
_FLAVOR_REF = CONF.compute.flavor_ref
|
|
||||||
|
|
||||||
# Inheriting TestCases should change this ssh User if needed.
|
|
||||||
_IMAGE_SSH_USER = CONF.validation.image_ssh_user
|
|
||||||
|
|
||||||
# suffix to use for the newly created flavors.
|
|
||||||
_FLAVOR_SUFFIX = ''
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def skip_checks(cls):
|
|
||||||
super(TestBase, cls).skip_checks()
|
|
||||||
# check if the configured hypervisor_version is higher than
|
|
||||||
# the test's required minimum Hyper-V version.
|
|
||||||
|
|
||||||
# TODO(claudiub): instead of expecting a config option specifying
|
|
||||||
# the hypervisor version, we could check nova's compute nodes for
|
|
||||||
# their hypervisor versions.
|
|
||||||
config_vers = CONF.hyperv.hypervisor_version
|
|
||||||
if config_vers < cls._MIN_HYPERV_VERSION:
|
|
||||||
msg = ('Configured hypervisor_version (%(config_vers)s) is not '
|
|
||||||
'supported. It must be higher than %(req_vers)s.' % {
|
|
||||||
'config_vers': config_vers,
|
|
||||||
'req_vers': cls._MIN_HYPERV_VERSION})
|
|
||||||
raise cls.skipException(msg)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup_clients(cls):
|
|
||||||
super(TestBase, cls).setup_clients()
|
|
||||||
# Compute client
|
|
||||||
cls.compute_fips_client = (
|
|
||||||
cls.os_primary.compute_floating_ips_client)
|
|
||||||
cls.keypairs_client = cls.os_primary.keypairs_client
|
|
||||||
cls.servers_client = cls.os_primary.servers_client
|
|
||||||
cls.admin_servers_client = cls.os_admin.servers_client
|
|
||||||
cls.admin_flavors_client = cls.os_admin.flavors_client
|
|
||||||
cls.admin_migrations_client = cls.os_admin.migrations_client
|
|
||||||
cls.admin_hypervisor_client = cls.os_admin.hypervisor_client
|
|
||||||
|
|
||||||
# Neutron network client
|
|
||||||
cls.security_groups_client = (
|
|
||||||
cls.os_primary.security_groups_client)
|
|
||||||
cls.security_group_rules_client = (
|
|
||||||
cls.os_primary.security_group_rules_client)
|
|
||||||
|
|
||||||
def create_floating_ip(self, server):
|
|
||||||
"""Create a floating IP and associates to a server on Nova"""
|
|
||||||
|
|
||||||
pool_name = CONF.network.floating_network_name
|
|
||||||
floating_ip = (
|
|
||||||
self.compute_fips_client.create_floating_ip(pool=pool_name))
|
|
||||||
floating_ip = floating_ip['floating_ip']
|
|
||||||
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
|
|
||||||
self.compute_fips_client.delete_floating_ip,
|
|
||||||
floating_ip['id'])
|
|
||||||
self.compute_fips_client.associate_floating_ip_to_server(
|
|
||||||
floating_ip['ip'], server['id'])
|
|
||||||
return floating_ip
|
|
||||||
|
|
||||||
def _get_image_ref(self):
|
|
||||||
return self._IMAGE_REF
|
|
||||||
|
|
||||||
def _flavor_cleanup(self, flavor_id):
|
|
||||||
try:
|
|
||||||
self.admin_flavors_client.delete_flavor(flavor_id)
|
|
||||||
self.admin_flavors_client.wait_for_resource_deletion(flavor_id)
|
|
||||||
except exceptions.NotFound:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _create_new_flavor(self, flavor_ref, flavor_updates):
|
|
||||||
"""Creates a new flavor based on the given flavor and flavor updates.
|
|
||||||
|
|
||||||
:returns: the newly created flavor's ID.
|
|
||||||
"""
|
|
||||||
flavor = self.admin_flavors_client.show_flavor(flavor_ref)['flavor']
|
|
||||||
|
|
||||||
flavor_name = 'test_resize'
|
|
||||||
if self._FLAVOR_SUFFIX:
|
|
||||||
flavor_name += '_%s' % self._FLAVOR_SUFFIX
|
|
||||||
|
|
||||||
new_flavor = self.admin_flavors_client.create_flavor(
|
|
||||||
name=data_utils.rand_name(flavor_name),
|
|
||||||
ram=flavor['ram'] + flavor_updates.get('ram', 0),
|
|
||||||
disk=flavor['disk'] + flavor_updates.get('disk', 0),
|
|
||||||
vcpus=flavor['vcpus'] + flavor_updates.get('vcpus', 0),
|
|
||||||
)['flavor']
|
|
||||||
|
|
||||||
self.addCleanup(self._flavor_cleanup, new_flavor['id'])
|
|
||||||
|
|
||||||
# Add flavor extra_specs, if needed.
|
|
||||||
extra_specs = flavor_updates.get('extra_specs')
|
|
||||||
if extra_specs:
|
|
||||||
self.admin_flavors_client.set_flavor_extra_spec(
|
|
||||||
new_flavor['id'], **extra_specs)
|
|
||||||
|
|
||||||
return new_flavor
|
|
||||||
|
|
||||||
def _get_flavor_ref(self):
|
|
||||||
return self._FLAVOR_REF
|
|
||||||
|
|
||||||
def _create_server(self, flavor=None):
|
|
||||||
"""Wrapper utility that returns a test server.
|
|
||||||
|
|
||||||
This wrapper utility calls the common create test server and
|
|
||||||
returns a test server.
|
|
||||||
"""
|
|
||||||
clients = self.os_primary
|
|
||||||
name = data_utils.rand_name(self.__class__.__name__ + "-server")
|
|
||||||
image_id = self._get_image_ref()
|
|
||||||
flavor = flavor or self._get_flavor_ref()
|
|
||||||
keypair = self.create_keypair()
|
|
||||||
tenant_network = self.get_tenant_network()
|
|
||||||
security_group = self._create_security_group()
|
|
||||||
# we need to pass the security group's name to the instance.
|
|
||||||
sg_group_names = [{'name': security_group['name']}]
|
|
||||||
|
|
||||||
body, _ = compute.create_test_server(
|
|
||||||
clients, name=name,
|
|
||||||
flavor=flavor,
|
|
||||||
image_id=image_id,
|
|
||||||
key_name=keypair['name'],
|
|
||||||
tenant_network=tenant_network,
|
|
||||||
security_groups=sg_group_names,
|
|
||||||
wait_until='ACTIVE')
|
|
||||||
|
|
||||||
self.addCleanup(waiters.wait_for_server_termination,
|
|
||||||
self.servers_client, body['id'])
|
|
||||||
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
|
|
||||||
self.servers_client.delete_server, body['id'])
|
|
||||||
server = clients.servers_client.show_server(body['id'])['server']
|
|
||||||
|
|
||||||
floating_ip = self.create_floating_ip(server)
|
|
||||||
server_tuple = Server_tuple(
|
|
||||||
server=server,
|
|
||||||
keypair=keypair,
|
|
||||||
floating_ip=floating_ip,
|
|
||||||
security_groups=[security_group])
|
|
||||||
|
|
||||||
return server_tuple
|
|
||||||
|
|
||||||
def _get_server_as_admin(self, server):
|
|
||||||
# only admins have access to certain instance properties.
|
|
||||||
return self.admin_servers_client.show_server(
|
|
||||||
server['id'])['server']
|
|
||||||
|
|
||||||
def _create_security_group(self):
|
|
||||||
sg_name = data_utils.rand_name(self.__class__.__name__)
|
|
||||||
sg_desc = sg_name + " description"
|
|
||||||
secgroup = self.security_groups_client.create_security_group(
|
|
||||||
name=sg_name, description=sg_desc)['security_group']
|
|
||||||
self.addCleanup(
|
|
||||||
test_utils.call_and_ignore_notfound_exc,
|
|
||||||
self.security_groups_client.delete_security_group,
|
|
||||||
secgroup['id'])
|
|
||||||
|
|
||||||
# Add rules to the security group
|
|
||||||
self._create_loginable_secgroup_rule(secgroup)
|
|
||||||
return secgroup
|
|
||||||
|
|
||||||
def _create_loginable_secgroup_rule(self, secgroup):
|
|
||||||
"""Create loginable security group rule
|
|
||||||
|
|
||||||
This function will create:
|
|
||||||
1. egress and ingress tcp port 22 allow rule in order to allow ssh
|
|
||||||
access for ipv4.
|
|
||||||
3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
|
|
||||||
"""
|
|
||||||
|
|
||||||
rulesets = [
|
|
||||||
# ssh
|
|
||||||
dict(protocol='tcp',
|
|
||||||
port_range_min=22,
|
|
||||||
port_range_max=22),
|
|
||||||
# ping
|
|
||||||
dict(protocol='icmp'),
|
|
||||||
]
|
|
||||||
for ruleset in rulesets:
|
|
||||||
for r_direction in ['ingress', 'egress']:
|
|
||||||
ruleset['direction'] = r_direction
|
|
||||||
self._create_security_group_rule(
|
|
||||||
secgroup, **ruleset)
|
|
||||||
|
|
||||||
def _create_security_group_rule(self, secgroup, **kwargs):
|
|
||||||
"""Create a rule from a dictionary of rule parameters.
|
|
||||||
|
|
||||||
Creates a rule in a secgroup.
|
|
||||||
|
|
||||||
:param secgroup: the security group.
|
|
||||||
:param kwargs: a dictionary containing rule parameters:
|
|
||||||
for example, to allow incoming ssh:
|
|
||||||
rule = {
|
|
||||||
direction: 'ingress'
|
|
||||||
protocol:'tcp',
|
|
||||||
port_range_min: 22,
|
|
||||||
port_range_max: 22
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
ruleset = dict(security_group_id=secgroup['id'],
|
|
||||||
tenant_id=secgroup['tenant_id'])
|
|
||||||
ruleset.update(kwargs)
|
|
||||||
|
|
||||||
sec_group_rules_client = self.security_group_rules_client
|
|
||||||
sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
|
|
||||||
sg_rule = sg_rule['security_group_rule']
|
|
||||||
|
|
||||||
return sg_rule
|
|
||||||
|
|
||||||
def _wait_for_server_status(self, server, status='ACTIVE'):
|
|
||||||
waiters.wait_for_server_status(self.servers_client, server['id'],
|
|
||||||
status)
|
|
||||||
|
|
||||||
def _get_server_client(self, server_tuple):
|
|
||||||
"""Get a SSH client to a remote server
|
|
||||||
|
|
||||||
:returns: RemoteClient object
|
|
||||||
"""
|
|
||||||
server = server_tuple.server
|
|
||||||
ip_address = server_tuple.floating_ip['ip']
|
|
||||||
private_key = server_tuple.keypair['private_key']
|
|
||||||
|
|
||||||
# ssh into the VM
|
|
||||||
username = self._IMAGE_SSH_USER
|
|
||||||
linux_client = remote_client.RemoteClient(
|
|
||||||
ip_address, username, pkey=private_key, password=None,
|
|
||||||
server=server, servers_client=self.servers_client)
|
|
||||||
linux_client.validate_authentication()
|
|
||||||
|
|
||||||
return linux_client
|
|
||||||
|
|
||||||
def _check_server_connectivity(self, server_tuple):
|
|
||||||
# if server connectivity works, an SSH client can be opened.
|
|
||||||
self._get_server_client(server_tuple)
|
|
||||||
|
|
||||||
def _check_scenario(self, server_tuple):
|
|
||||||
# NOTE(claudiub): This method is to be used when verifying a
|
|
||||||
# particular scenario. If a scenario test case needs to perform
|
|
||||||
# different validation steps (e.g.: metrics collection), it should
|
|
||||||
# overwrite this method.
|
|
||||||
self._check_server_connectivity(server_tuple)
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
upgrade:
|
|
||||||
- |
|
|
||||||
Python 2.7 support has been dropped. Last release of oswin-tempest-plugin
|
|
||||||
to support py2.7 is OpenStack Train. The minimum version of Python now
|
|
||||||
supported by oswin-tempest-plugin is Python 3.5.
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
upgrade:
|
|
||||||
- |
|
|
||||||
Python 3.6 & 3.7 support has been dropped. The minimum version of Python now
|
|
||||||
supported is Python 3.8.
|
|
@ -1,11 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
|
||||||
|
|
||||||
oslo.config>=5.1.0 # Apache-2.0
|
|
||||||
oslo.log>=3.36.0 # Apache-2.0
|
|
||||||
oslo.utils>=3.33.0 # Apache-2.0
|
|
||||||
tempest>=17.1.0 # Apache-2.0
|
|
||||||
pywinrm>=0.2.2 # MIT
|
|
27
setup.cfg
27
setup.cfg
@ -1,27 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = oswin-tempest-plugin
|
|
||||||
summary = This project contains Tempest tests to cover the os_win project, as well as a plugin to automatically load these tests into Tempest.
|
|
||||||
description_file =
|
|
||||||
README.rst
|
|
||||||
author = Cloudbase Solutions
|
|
||||||
author_email = info@cloudbasesolutions.com
|
|
||||||
home_page = https://opendev.org/openstack/oswin-tempest-plugin
|
|
||||||
python_requires = >=3.8
|
|
||||||
classifier =
|
|
||||||
Environment :: OpenStack
|
|
||||||
Intended Audience :: Information Technology
|
|
||||||
Intended Audience :: System Administrators
|
|
||||||
License :: OSI Approved :: Apache Software License
|
|
||||||
Operating System :: POSIX :: Linux
|
|
||||||
Operating System :: Microsoft :: Windows
|
|
||||||
Programming Language :: Python
|
|
||||||
Programming Language :: Python :: 3
|
|
||||||
Programming Language :: Python :: 3.8
|
|
||||||
|
|
||||||
[files]
|
|
||||||
packages =
|
|
||||||
oswin_tempest_plugin
|
|
||||||
|
|
||||||
[entry_points]
|
|
||||||
tempest.test_plugins =
|
|
||||||
oswin_tempest_plugin = oswin_tempest_plugin.plugin:OSWinTempestPlugin
|
|
29
setup.py
29
setup.py
@ -1,29 +0,0 @@
|
|||||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
|
||||||
import setuptools
|
|
||||||
|
|
||||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
|
||||||
# setuptools if some other modules registered functions in `atexit`.
|
|
||||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
|
||||||
try:
|
|
||||||
import multiprocessing # noqa
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['pbr>=2.0.0'],
|
|
||||||
pbr=True)
|
|
@ -1,13 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
hacking>=3.0.1,<3.1.0 # Apache-2.0
|
|
||||||
|
|
||||||
coverage!=4.4,>=4.0 # Apache-2.0
|
|
||||||
python-subunit>=1.0.0 # Apache-2.0/BSD
|
|
||||||
stestr>=2.0.0 # Apache-2.0
|
|
||||||
oslotest>=3.2.0 # Apache-2.0
|
|
||||||
testscenarios>=0.4 # Apache-2.0/BSD
|
|
||||||
testtools>=2.2.0 # MIT
|
|
||||||
|
|
43
tox.ini
43
tox.ini
@ -1,43 +0,0 @@
|
|||||||
[tox]
|
|
||||||
minversion = 3.1.1
|
|
||||||
envlist = py3,pypy,pep8
|
|
||||||
skipsdist = True
|
|
||||||
ignore_basepython_conflict = True
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
basepython = python3
|
|
||||||
usedevelop = True
|
|
||||||
setenv =
|
|
||||||
VIRTUAL_ENV={envdir}
|
|
||||||
PYTHONWARNINGS=default::DeprecationWarning
|
|
||||||
deps =
|
|
||||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
|
||||||
-r{toxinidir}/requirements.txt
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
commands = stestr run --slowest {posargs}
|
|
||||||
|
|
||||||
[testenv:pep8]
|
|
||||||
commands = flake8 {posargs}
|
|
||||||
|
|
||||||
[testenv:venv]
|
|
||||||
commands = {posargs}
|
|
||||||
|
|
||||||
[testenv:cover]
|
|
||||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
|
||||||
|
|
||||||
[testenv:docs]
|
|
||||||
deps =
|
|
||||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
|
||||||
-r{toxinidir}/doc/requirements.txt
|
|
||||||
commands = sphinx-build -W -b html doc/source doc/build/html
|
|
||||||
|
|
||||||
[testenv:debug]
|
|
||||||
commands = oslo_debug_helper {posargs}
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
# E123, E125 skipped as they are invalid PEP-8.
|
|
||||||
# W503 line break before binary operator
|
|
||||||
show-source = True
|
|
||||||
ignore = E123,E125,W503
|
|
||||||
builtins = _
|
|
||||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
|
Loading…
Reference in New Issue
Block a user