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:
Ghanshyam Mann 2023-09-09 12:30:15 -07:00
parent 9e95d33d9e
commit 0976964257
47 changed files with 9 additions and 2242 deletions

View File

@ -1,6 +0,0 @@
[run]
branch = True
source = oswin_tempest_plugin
[report]
ignore_errors = True

56
.gitignore vendored
View File

@ -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?

View File

@ -1,3 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>

View File

@ -1,3 +0,0 @@
[DEFAULT]
test_path=${OS_TEST_PATH:-./oswin_tempest_plugin/tests}
top_dir=./

View File

@ -1,5 +0,0 @@
- project:
templates:
- openstack-python3-zed-jobs
- check-requirements
queue: os-win

View File

@ -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

View File

@ -1,4 +0,0 @@
oswin-tempest-plugin Style Commandments
=======================================
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/

176
LICENSE
View File

@ -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.

View File

@ -1,19 +1,10 @@
====================
oswin-tempest-plugin
====================
This project is no longer maintained.
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".
This project contains Tempest tests to cover the os_win project, as well as a plugin to automatically load these tests into Tempest.
Please fill here a long description which must be at least 3 lines wrapped on
80 cols, so that distribution package maintainers can use it in their packages.
Note that this is a hard requirement.
* 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
For any further questions, please email
openstack-discuss@lists.openstack.org or join #openstack-dev on
OFTC.

View File

@ -1,2 +0,0 @@
[python: **.py]

View File

@ -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

View File

@ -1,2 +0,0 @@
sphinx>=2.0.0,!=2.1.0 # BSD
openstackdocstheme>=2.2.1 # Apache-2.0

View File

@ -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}

View File

@ -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>`_.

View File

@ -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`

View File

@ -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

View File

@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@ -1,7 +0,0 @@
=====
Usage
=====
To use oswin-tempest-plugin in a project::
import oswin_tempest_plugin

View File

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

View File

@ -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

View File

@ -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')

View File

@ -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]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.

View File

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

View File

@ -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)}}

View File

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

View File

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

View File

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

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

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

View File

@ -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
View File

@ -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