Retire monasca-tempest-plugin repository
This repository is being retired as part of the Monasca project retirement. The project content has been replaced with a retirement notice. Needed-By: I3cb522ce8f51424b64e93c1efaf0dfd1781cd5ac Change-Id: I85144c7d4d254f90c60971e5f0ebab17c481df3e Signed-off-by: Goutham Pacha Ravi <gouthampravi@gmail.com>
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = monasca_tempest_plugin
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
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=./monasca_tempest_plugin/tests
|
||||
top_dir=./
|
||||
69
.zuul.yaml
69
.zuul.yaml
@@ -1,69 +0,0 @@
|
||||
|
||||
- project:
|
||||
queue: monasca
|
||||
templates:
|
||||
- check-requirements
|
||||
- tempest-plugin-jobs
|
||||
- publish-openstack-docs-pti
|
||||
check:
|
||||
jobs:
|
||||
- monasca-tempest-python3-influxdb:
|
||||
voting: false
|
||||
- monasca-tempest-python3-cassandra:
|
||||
voting: false
|
||||
- monasca-tempest-log-python3-influxdb:
|
||||
voting: false
|
||||
- build-monasca-docker-image:
|
||||
voting: false
|
||||
gate:
|
||||
jobs:
|
||||
- monasca-tempest-python3-influxdb:
|
||||
voting: false
|
||||
- monasca-tempest-log-python3-influxdb:
|
||||
voting: false
|
||||
post:
|
||||
jobs:
|
||||
- publish-monasca-tempest-plugin-docker-image
|
||||
periodic:
|
||||
jobs:
|
||||
- publish-monasca-tempest-plugin-docker-image
|
||||
release:
|
||||
jobs:
|
||||
- publish-monasca-tempest-plugin-docker-image
|
||||
|
||||
- job:
|
||||
name: publish-monasca-tempest-plugin-docker-image
|
||||
parent: build-monasca-docker-image
|
||||
post-run: playbooks/docker-publish.yml
|
||||
required-projects:
|
||||
- openstack/monasca-common
|
||||
vars:
|
||||
publisher: true
|
||||
secrets:
|
||||
- doker_hub_login_tempest
|
||||
|
||||
- secret:
|
||||
name: doker_hub_login_tempest
|
||||
data:
|
||||
user: !encrypted/pkcs1-oaep
|
||||
- TB40TL1BT0pn4kcfHVZqtu+7cuRXM2OJYmHUDdpAVjdwiZvhTu6W0e2jWGv5knNXsPI+n
|
||||
cz0LB/oYxg5TGl5WHpz9If66dW1lW9HwjlAYOyizzTmz+AfwNShUDR6W39rfxhjxY5bxu
|
||||
20FBJnhmcAZvR6iQas7nuD+8PhoFlNAGhk0y9r7qCzVzgM4A2icVHor9xk7UGAyC3HgLN
|
||||
u5QFhaUmZ32r0pNOnVQF1NgHIBWMk+De6NTr/hw9jXrGOQJYysG6GPKRRqNgXAvUpmykz
|
||||
Tu7hP17EwgRVxWqwheqEAzIPOGSpWlF32nKJ63yIiMRlkxp3guZMu3/oVDNODoy05WaLc
|
||||
fO/WeF/pCj+lAJWrC/1diDMnuA4vRgC1ME/f4DANFHjIO3ir0M38IafZW5HbCC0LzFNaE
|
||||
yJH/0AWwU8O0XAU62Rpr7n57BFib80ViPGkJZ4FOQE0DByEk/BgV0diex4CBlk+AOhOVo
|
||||
K3Jh238mYdNVFxDqiYCsZNgIkIkSH5hBS2CU2bTcE5856Ep2c7syzTQVwszl34DJk6+ws
|
||||
pzbGQaFxFpO5B1gnZC4GMMT/uFjAaQGMpSN3sGY1R/Jqs0E/moP227y/WS47YdcXA+t2n
|
||||
OxuUsTT8qqZ6gmGtRiqgBUqSxNKBdemfXMmiI2nGmHMii/LG5Sw+x5ma2zPyzs=
|
||||
password: !encrypted/pkcs1-oaep
|
||||
- L3MF9SSBol2o85/7AUWkkaJQtNo6hFKTruK1JaCpiTojAcNVWIYatny7hFoftWiTkMJfC
|
||||
VncmLDg6rBZCyB+lhq77CmrvP9Z4HHGlqmUC9HzTx13fTQKUKQsY3fz2reTiO79XlvI2k
|
||||
z7NzubH8IPj1461kMXL1tSbE/ESwzp7aIqmALJ4pf73sC+YIx7LbW3zwqEWNf61w+/OHt
|
||||
03hTCidO98KgZQHV25r0EeCx9ts8BSUamLppsWkq4XzkN7gmUs0BvPdCP+bFltnAbyhwQ
|
||||
m7r7ga1bZ7cWVxdH4bezync+jWHO1IUxO90erFf7WuvfAYEA85SfDAuqNVF7HJvL+gKDt
|
||||
wSIybJtHiAUOj9a0/ZoHMBHf7GW1/PDkdJ/HYP5RRRwYp+8YQkb1DYE2USUGhPWr0Vx/U
|
||||
/LTsBmMfV0GHbGTlNNVRoM2axgreyjX8Ioj+08CXsKnyY1x6V2giRvONaKqt4b+kSGgRj
|
||||
2j6CpWYUysJAjwVMYviI/cj1/4kyrdIajQQh7QuC+ESPd/8f6ijmyF9pBlbeBJSHnLMDv
|
||||
ZCufss2XmedqxiyPgjq9HbNN7TNOYAWYI8u9FoojR6UTjNfGx9fZ7jmBas5KFC2BAkZo1
|
||||
HkFvJgA8USrM7U9LRAQnuRTSxFcqPyUKyyixXVpA7S56TO9GiHPhcAngEUtMSA=
|
||||
@@ -1,17 +0,0 @@
|
||||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps in this page:
|
||||
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
If you already have a good understanding of how the system works and your
|
||||
OpenStack accounts are set up, you can skip to the development workflow
|
||||
section of this documentation to learn how changes to OpenStack should be
|
||||
submitted for review via the Gerrit tool:
|
||||
|
||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on StoryBoard, not GitHub:
|
||||
|
||||
https://storyboard.openstack.org/#!/project/919
|
||||
@@ -1,4 +0,0 @@
|
||||
openstack Style Commandments
|
||||
============================
|
||||
|
||||
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
|
||||
18
README.rst
18
README.rst
@@ -1,13 +1,9 @@
|
||||
======================
|
||||
Monasca Tempest Plugin
|
||||
======================
|
||||
This project is no longer maintained.
|
||||
|
||||
Tempest plugin for Monasca API and Log API Project
|
||||
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".
|
||||
|
||||
It contains the tempest plugin for the functional testing of Monasca Project.
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: https://docs.openstack.org/monasca-tempest-plugin/latest/
|
||||
* Release notes: https://docs.openstack.org/releasenotes/monasca-api/
|
||||
* Source: https://opendev.org/openstack/monasca-tempest-plugin
|
||||
* Bugs: https://storyboard.openstack.org/#!/project/919
|
||||
For any further questions, please email openstack-discuss@lists.openstack.org
|
||||
or join #openstack-dev on OFTC.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
====================
|
||||
Enabling in Devstack
|
||||
====================
|
||||
|
||||
**WARNING**: the stack.sh script must be run in a disposable VM that is not
|
||||
being created automatically, see the README.md file in the "devstack"
|
||||
repository.
|
||||
|
||||
1. Download DevStack::
|
||||
|
||||
git clone https://opendev.org/openstack/devstack.git
|
||||
cd devstack
|
||||
|
||||
2. Add this repo as an external repository::
|
||||
|
||||
> cat local.conf
|
||||
[[local|localrc]]
|
||||
enable_plugin monasca-tempest-plugin https://opendev.org/openstack/monasca-tempest-plugin
|
||||
|
||||
3. run ``stack.sh``
|
||||
|
||||
Running Monasca tempest tests
|
||||
=============================
|
||||
|
||||
1. Listing Monasca tempest tests::
|
||||
|
||||
tempest list-plugins
|
||||
|
||||
2. Running monasca tempest tests::
|
||||
|
||||
cd /opt/stack/tempest
|
||||
tempest run -r monasca_tempest_tests.tests.api
|
||||
tempest run -r monasca_tempest_tests.tests.log_api
|
||||
@@ -1,13 +0,0 @@
|
||||
# install_monasca_tempest_plugin
|
||||
function install_monasca_tempest_plugin {
|
||||
setup_dev_lib "monasca-tempest-plugin"
|
||||
}
|
||||
|
||||
if [[ "$1" == "stack" ]]; then
|
||||
case "$2" in
|
||||
install)
|
||||
echo_summary "Installing monasca-tempest-plugin"
|
||||
install_monasca_tempest_plugin
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
@@ -1,3 +0,0 @@
|
||||
GITREPO["monasca-tempest-plugin"]=${MONASCA_TEMPEST_REPO:-${GIT_BASE}/openstack/monasca-tempest-plugin.git}
|
||||
GITDIR["monasca-tempest-plugin"]=$DEST/monasca-tempest-plugin
|
||||
GITBRANCH["monasca-tempest-plugin"]=master
|
||||
@@ -1,4 +0,0 @@
|
||||
sphinx>=2.0.0,!=2.1.0 # BSD
|
||||
openstackdocstheme>=2.2.1 # Apache-2.0
|
||||
# releasenotes
|
||||
reno>=3.1.0 # Apache-2.0
|
||||
@@ -1,79 +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',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
|
||||
# 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 = 'monasca-tempest-plugin'
|
||||
copyright = '2017, OpenStack Developers'
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/monasca-tempest-plugin'
|
||||
openstackdocs_use_storyboard = True
|
||||
|
||||
# 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,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Developers', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
@@ -1,18 +0,0 @@
|
||||
======================================================
|
||||
Welcome to the documentation of monasca_tempest_plugin
|
||||
======================================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
readme
|
||||
installation
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -1,13 +0,0 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
Tempest automatically discovers installed plugins. That's why you just need
|
||||
to install the Python packages that contains the Monasca Tempest plugin in
|
||||
the same environment where Tempest is installed.
|
||||
|
||||
At the command line::
|
||||
|
||||
$ git clone https://opendev.org/openstack/monasca-tempest-plugin
|
||||
$ cd monasca-tempest-plugin/
|
||||
$ pip install monasca-tempest-plugin
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: ../../README.rst
|
||||
@@ -1,46 +0,0 @@
|
||||
ARG DOCKER_IMAGE=monasca/tempest-tests
|
||||
ARG APP_REPO=https://review.opendev.org/openstack/monasca-tempest-plugin
|
||||
|
||||
# Branch, tag or git hash to build from.
|
||||
ARG REPO_VERSION=master
|
||||
ARG CONSTRAINTS_BRANCH=master
|
||||
|
||||
# Extra Python3 dependencies.
|
||||
ARG EXTRA_DEPS="python-openstackclient python-monascaclient"
|
||||
|
||||
# Always start from `monasca-base` image and use specific tag of it.
|
||||
ARG BASE_TAG=master
|
||||
FROM monasca/base:$BASE_TAG
|
||||
|
||||
# Environment variables used for our service or wait scripts.
|
||||
ENV \
|
||||
KEYSTONE_IDENTITY_URI=http://keystone:35357 \
|
||||
USE_DYNAMIC_CREDS=True \
|
||||
KEYSTONE_ADMIN_USER=mini-mon \
|
||||
KEYSTONE_ADMIN_PASSWORD=password \
|
||||
KEYSTONE_ADMIN_PROJECT=mini-mon \
|
||||
KEYSTONE_ADMIN_DOMAIN=Default \
|
||||
OS_AUTH_URL=http://keystone:35357/v3 \
|
||||
OS_USERNAME=mini-mon \
|
||||
OS_PASSWORD=password \
|
||||
OS_TENANT_NAME=mini-mon \
|
||||
OS_DOMAIN_NAME=Default \
|
||||
MONASCA_WAIT_FOR_API=true \
|
||||
MONASCA_API_WAIT_RETRIES=24 \
|
||||
MONASCA_API_WAIT_INTERVAL=5 \
|
||||
STAY_ALIVE_ON_FAILURE=false
|
||||
|
||||
# Copy all neccessary files to proper locations.
|
||||
COPY tempest.conf.j2 /etc/tempest/
|
||||
|
||||
WORKDIR /tempest
|
||||
|
||||
# Run here all additionals steps your service need post installation.
|
||||
# Stay with only one `RUN` and use `&& \` for next steps to don't create
|
||||
# unnecessary image layers. Clean at the end to conserve space.
|
||||
#RUN \
|
||||
# echo "Some steps to do after main installation." && \
|
||||
# echo "Hello when building."
|
||||
|
||||
# Implement start script in `start.sh` file.
|
||||
CMD ["/start.sh"]
|
||||
@@ -1,122 +0,0 @@
|
||||
===============================================
|
||||
Docker image for Monasca Monasca Temptest tests
|
||||
===============================================
|
||||
This image could be used for running Tempest tests on Monasca installed in any
|
||||
way. After providing proper environment variables container could be started
|
||||
and will run tests based on what endpoints it will find in configured Keystone.
|
||||
Supported endpoints are ``monitoring`` and ``logs`` (``Service Type`` in
|
||||
``openstack endpoint list`` output).
|
||||
|
||||
|
||||
Building monasca-base image
|
||||
===========================
|
||||
See https://github.com/openstack/monasca-common/tree/master/docker/README.rst
|
||||
|
||||
|
||||
Building Docker image
|
||||
=====================
|
||||
|
||||
Example:
|
||||
$ ./build_image.sh <repository_version> <upper_constrains_branch> <common_version>
|
||||
|
||||
Everything after ``./build_image.sh`` is optional and by default configured
|
||||
to get versions from ``Dockerfile``. ``./build_image.sh`` also contain more
|
||||
detailed build description.
|
||||
|
||||
|
||||
How to start
|
||||
~~~~~~~~~~~~
|
||||
|
||||
To run, this container needs to connect to a working Keystone and Monasca API.
|
||||
|
||||
When using running Monasca in Docker you can connect this image to the network
|
||||
where Monasca is accessible and run all tests.
|
||||
Find network on machine with Monasca with ``docker network ls``.
|
||||
For example you can see similar information to:
|
||||
``e20533f6112c monasca-docker_default bridge local``
|
||||
|
||||
Using this network run all tempest tests with following command:
|
||||
|
||||
``docker run -it --rm --network=monasca-docker_default monasca/tempest-tests:master``
|
||||
|
||||
It's important to configure all necessary connection environment variables.
|
||||
They are listed in the next two sections.
|
||||
|
||||
Example command to run tempest tests with custom variables::
|
||||
|
||||
``docker run -it --rm --network=monasca-docker_default --env-file=tempest_con.env monasca/tempest-tests:master``
|
||||
|
||||
In this example you configure all environment variables in ``tempest_con.env``
|
||||
file::
|
||||
|
||||
KEYSTONE_IDENTITY_URI=http://172.17.0.1:35357
|
||||
USE_DYNAMIC_CREDS=True
|
||||
KEYSTONE_ADMIN_USER=mini-mon
|
||||
KEYSTONE_ADMIN_PASSWORD=password
|
||||
KEYSTONE_ADMIN_PROJECT=mini-mon
|
||||
KEYSTONE_ADMIN_DOMAIN=Default
|
||||
OS_AUTH_URL=http://172.17.0.1:35357/v3
|
||||
OS_USERNAME=mini-mon
|
||||
OS_PASSWORD=password
|
||||
OS_PROJECT_NAME=mini-mon
|
||||
OS_DOMAIN_NAME=Default
|
||||
|
||||
In order to run in docker-compose add this section to docker-compose.yaml::
|
||||
|
||||
tempest-tests:
|
||||
image: monasca/tempest-tests:master
|
||||
environment:
|
||||
KEYSTONE_IDENTITY_URI: "http://keystone:35357"
|
||||
|
||||
|
||||
Environment variables
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
========================= ============================== ==========================================
|
||||
Variable Default Description
|
||||
========================= ============================== ==========================================
|
||||
USE_DYNAMIC_CREDS True Create dynamic credentials for tests
|
||||
KEYSTONE_ADMIN_USER mini-mon OpenStack administrator user name
|
||||
KEYSTONE_ADMIN_PASSWORD password OpenStack administrator user password
|
||||
KEYSTONE_ADMIN_PROJECT mini-mon OpenStack administrator tenant name
|
||||
KEYSTONE_ADMIN_DOMAIN Default OpenStack administrator domain
|
||||
OS_AUTH_URL http://keystone:35357/v3 Versioned Keystone URL
|
||||
OS_USERNAME mini-mon Keystone user name
|
||||
OS_PASSWORD password Keystone user password
|
||||
OS_PROJECT_NAME mini-mon Keystone user project name
|
||||
OS_DOMAIN_NAME Default Keystone user domain name
|
||||
IDENTITY_URI http://keystone:35357/v2.0/ Full URI of the Keystone, v2
|
||||
IDENTITY_URI_V3 http://keystone:35357/v3/ Full URI of the Keystone, v3
|
||||
LOG_LEVEL INFO Log level for root logging
|
||||
STAY_ALIVE_ON_FAILURE false If true, container runs 2 hours after service fail
|
||||
========================= ============================== ==========================================
|
||||
|
||||
|
||||
Wait scripts environment variables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
========================= =========================== =============================================
|
||||
Variable Default Description
|
||||
========================= =========================== =============================================
|
||||
KEYSTONE_IDENTITY_URI http://keystone:35357 URI to Keystone admin endpoint
|
||||
MONASCA_WAIT_FOR_API true If true, ensure Monasca API is available
|
||||
MONASCA_API_WAIT_RETRIES 24 Retries for Monasca API availability checks
|
||||
MONASCA_API_WAIT_INTERVAL 5 Sleep time between Monasca API retries
|
||||
========================= =========================== =============================================
|
||||
|
||||
|
||||
Scripts
|
||||
~~~~~~~
|
||||
start.sh
|
||||
In this starting script provide all steps that lead to the proper service
|
||||
start. Including usage of wait scripts and templating of configuration
|
||||
files. You also could provide the ability to allow running container after
|
||||
service died for easier debugging.
|
||||
|
||||
health_check.py
|
||||
This file will be used for checking the status of the application.
|
||||
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
https://docs.openstack.org/monasca-api/latest/
|
||||
|
||||
https://github.com/openstack/monasca-api/blob/master/README.rst
|
||||
@@ -1,150 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# TODO(Dobroslaw): move this script to monasca-common/docker folder
|
||||
# and leave here small script to download it and execute using env variables
|
||||
# to minimize code duplication.
|
||||
|
||||
set -x # Print each script step.
|
||||
set -eo pipefail # Exit the script if any statement returns error.
|
||||
|
||||
# This script is used for building Docker image with proper labels
|
||||
# and proper version of monasca-common.
|
||||
#
|
||||
# Example usage:
|
||||
# $ ./build_image.sh <repository_version> <upper_constains_branch> <common_version>
|
||||
#
|
||||
# Everything after `./build_image.sh` is optional and by default configured
|
||||
# to get versions from `Dockerfile`.
|
||||
#
|
||||
# To build from master branch (default):
|
||||
# $ ./build_image.sh
|
||||
# To build specific version run this script in the following way:
|
||||
# $ ./build_image.sh stable/queens
|
||||
# Building from specific commit:
|
||||
# $ ./build_image.sh cb7f226
|
||||
# When building from a tag monasca-common will be used in version available
|
||||
# in upper constraint file:
|
||||
# $ ./build_image.sh 2.5.0
|
||||
# To build image from Gerrit patch sets that is targeting branch stable/queens:
|
||||
# $ ./build_image.sh refs/changes/51/558751/1 stable/queens
|
||||
#
|
||||
# If you want to build image with custom monasca-common version you need
|
||||
# to provide it as in the following example:
|
||||
# $ ./build_image.sh master master refs/changes/19/595719/3
|
||||
|
||||
# Go to folder with Docker files.
|
||||
REAL_PATH=$(python3 -c "import os,sys; print(os.path.realpath('$0'))")
|
||||
cd "$(dirname "$REAL_PATH")/../docker/"
|
||||
|
||||
[ -z "$DOCKER_IMAGE" ] && \
|
||||
DOCKER_IMAGE=$(\grep DOCKER_IMAGE Dockerfile | cut -f2 -d"=")
|
||||
|
||||
: "${REPO_VERSION:=$1}"
|
||||
[ -z "$REPO_VERSION" ] && \
|
||||
REPO_VERSION=$(\grep REPO_VERSION Dockerfile | cut -f2 -d"=")
|
||||
# Let's stick to more readable version and disable SC2001 here.
|
||||
# shellcheck disable=SC2001
|
||||
REPO_VERSION_CLEAN=$(echo "$REPO_VERSION" | sed 's|/|-|g')
|
||||
|
||||
[ -z "$APP_REPO" ] && APP_REPO=$(\grep APP_REPO Dockerfile | cut -f2 -d"=")
|
||||
GITHUB_REPO=$(echo "$APP_REPO" | sed 's/review.opendev.org/github.com/' | \
|
||||
sed 's/ssh:/https:/')
|
||||
|
||||
if [ -z "$CONSTRAINTS_FILE" ]; then
|
||||
CONSTRAINTS_FILE=$(\grep CONSTRAINTS_FILE Dockerfile | cut -f2 -d"=") || true
|
||||
: "${CONSTRAINTS_FILE:=https://releases.openstack.org/constraints/upper/master}"
|
||||
fi
|
||||
|
||||
: "${CONSTRAINTS_BRANCH:=$2}"
|
||||
[ -z "$CONSTRAINTS_BRANCH" ] && \
|
||||
CONSTRAINTS_BRANCH=$(\grep CONSTRAINTS_BRANCH Dockerfile | cut -f2 -d"=")
|
||||
|
||||
# When using stable version of repository use same stable constraints file.
|
||||
case "$REPO_VERSION" in
|
||||
*stable*)
|
||||
CONSTRAINTS_BRANCH_CLEAN="$REPO_VERSION"
|
||||
CONSTRAINTS_FILE=${CONSTRAINTS_FILE/master/$CONSTRAINTS_BRANCH_CLEAN}
|
||||
# Get monasca-common version from stable upper constraints file.
|
||||
CONSTRAINTS_TMP_FILE=$(mktemp)
|
||||
wget --output-document "$CONSTRAINTS_TMP_FILE" \
|
||||
$CONSTRAINTS_FILE
|
||||
UPPER_COMMON=$(\grep 'monasca-common' "$CONSTRAINTS_TMP_FILE")
|
||||
# Get only version part from monasca-common.
|
||||
UPPER_COMMON_VERSION="${UPPER_COMMON##*===}"
|
||||
rm -rf "$CONSTRAINTS_TMP_FILE"
|
||||
;;
|
||||
*)
|
||||
CONSTRAINTS_BRANCH_CLEAN="$CONSTRAINTS_BRANCH"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Monasca-common variables.
|
||||
if [ -z "$COMMON_REPO" ]; then
|
||||
COMMON_REPO=$(\grep COMMON_REPO Dockerfile | cut -f2 -d"=") || true
|
||||
: "${COMMON_REPO:=https://review.opendev.org/openstack/monasca-common}"
|
||||
fi
|
||||
: "${COMMON_VERSION:=$3}"
|
||||
if [ -z "$COMMON_VERSION" ]; then
|
||||
COMMON_VERSION=$(\grep COMMON_VERSION Dockerfile | cut -f2 -d"=") || true
|
||||
if [ "$UPPER_COMMON_VERSION" ]; then
|
||||
# Common from upper constraints file.
|
||||
COMMON_VERSION="$UPPER_COMMON_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clone project to temporary directory for getting proper commit number from
|
||||
# branches and tags. We need this for setting proper image labels.
|
||||
# Docker does not allow to get any data from inside of system when building
|
||||
# image.
|
||||
TMP_DIR=$(mktemp -d)
|
||||
(
|
||||
cd "$TMP_DIR"
|
||||
# This many steps are needed to support gerrit patch sets.
|
||||
git init
|
||||
git remote add origin "$APP_REPO"
|
||||
git fetch origin "$REPO_VERSION"
|
||||
git reset --hard FETCH_HEAD
|
||||
)
|
||||
GIT_COMMIT=$(git -C "$TMP_DIR" rev-parse HEAD)
|
||||
[ -z "${GIT_COMMIT}" ] && echo "No git commit hash found" && exit 1
|
||||
rm -rf "$TMP_DIR"
|
||||
|
||||
# Do the same for monasca-common.
|
||||
COMMON_TMP_DIR=$(mktemp -d)
|
||||
(
|
||||
cd "$COMMON_TMP_DIR"
|
||||
# This many steps are needed to support gerrit patch sets.
|
||||
git init
|
||||
git remote add origin "$COMMON_REPO"
|
||||
git fetch origin "$COMMON_VERSION"
|
||||
git reset --hard FETCH_HEAD
|
||||
)
|
||||
COMMON_GIT_COMMIT=$(git -C "$COMMON_TMP_DIR" rev-parse HEAD)
|
||||
[ -z "${COMMON_GIT_COMMIT}" ] && echo "No git commit hash found" && exit 1
|
||||
rm -rf "$COMMON_TMP_DIR"
|
||||
|
||||
CREATION_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
docker build --no-cache \
|
||||
--build-arg CREATION_TIME="$CREATION_TIME" \
|
||||
--build-arg GITHUB_REPO="$GITHUB_REPO" \
|
||||
--build-arg APP_REPO="$APP_REPO" \
|
||||
--build-arg REPO_VERSION="$REPO_VERSION" \
|
||||
--build-arg GIT_COMMIT="$GIT_COMMIT" \
|
||||
--build-arg CONSTRAINTS_FILE="$CONSTRAINTS_FILE" \
|
||||
--build-arg COMMON_REPO="$COMMON_REPO" \
|
||||
--build-arg COMMON_VERSION="$COMMON_VERSION" \
|
||||
--build-arg COMMON_GIT_COMMIT="$COMMON_GIT_COMMIT" \
|
||||
--tag "$DOCKER_IMAGE":"$REPO_VERSION_CLEAN" .
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# (C) Copyright 2018 FUJITSU LIMITED
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Health check will returns 0 when service is working properly."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
LOG_LEVEL = logging.getLevelName(os.environ.get('LOG_LEVEL', 'INFO'))
|
||||
logging.basicConfig(level=LOG_LEVEL)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main():
|
||||
logger.debug('No health check for tempest')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
# Starting script.
|
||||
# All checks and configuration templating you need to do before service
|
||||
# could be safely started should be added in this file.
|
||||
|
||||
set -x
|
||||
|
||||
set -eo pipefail # Exit the script if any statement returns error.
|
||||
|
||||
# Test services we need before starting our service.
|
||||
echo "Start script: waiting for needed services"
|
||||
/wait_for.sh "$KEYSTONE_IDENTITY_URI"
|
||||
|
||||
if [ "$MONASCA_WAIT_FOR_API" = "true" ]; then
|
||||
echo "Waiting for Monasca API to become available..."
|
||||
success="false"
|
||||
|
||||
for i in $(seq "$MONASCA_API_WAIT_RETRIES"); do
|
||||
if monasca --os-user-domain-name "${OS_DOMAIN_NAME}" \
|
||||
--os-project-name "${OS_TENANT_NAME}" --os-auth-url "${OS_AUTH_URL}" \
|
||||
--os-username "${OS_USERNAME}" --os-password "${OS_PASSWORD}" \
|
||||
alarm-list --limit 1; then
|
||||
success="true"
|
||||
break
|
||||
else
|
||||
echo "Monasca API not yet ready (attempt $i of $MONASCA_API_WAIT_RETRIES)"
|
||||
sleep "$MONASCA_API_WAIT_INTERVAL"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$success" != "true" ]; then
|
||||
echo "Monasca API failed to become ready, exiting..."
|
||||
sleep 1
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Template all config files before start, it will use env variables.
|
||||
# Read usage examples: https://pypi.org/project/Templer/
|
||||
echo "Start script: creating config files from templates"
|
||||
templer -v -f /etc/tempest/tempest.conf.j2 /etc/tempest/tempest.conf
|
||||
|
||||
# Start our service.
|
||||
echo "Start script: starting tempest"
|
||||
cd /tempest/
|
||||
# Initialize only when folder is empty.
|
||||
if [ ! "$(ls -A /tempest/)" ]; then
|
||||
tempest init
|
||||
fi
|
||||
tempest list-plugins
|
||||
|
||||
if openstack endpoint list --column "Service Type" -f value | grep -q monitoring
|
||||
then
|
||||
tempest run -r monasca_tempest_tests.tests.api
|
||||
else
|
||||
true
|
||||
fi
|
||||
if openstack endpoint list --column "Service Type" -f value | grep -q logs
|
||||
then
|
||||
tempest run -r monasca_tempest_tests.tests.log_api
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# Allow server to stay alive in case of failure for 2 hours for debugging.
|
||||
RESULT=$?
|
||||
if [ $RESULT != 0 ] && [ "$STAY_ALIVE_ON_FAILURE" = "true" ]; then
|
||||
echo "Service died, waiting 120 min before exiting"
|
||||
sleep 7200
|
||||
fi
|
||||
exit $RESULT
|
||||
@@ -1,11 +0,0 @@
|
||||
[identity]
|
||||
auth_version = v3
|
||||
uri = {{ AUTH_URI | default(KEYSTONE_IDENTITY_URI + "/v2.0/") }}
|
||||
uri_v3 = {{ AUTH_URI_V3 | default(KEYSTONE_IDENTITY_URI + "/v3/") }}
|
||||
|
||||
[auth]
|
||||
use_dynamic_credentials = {{ USE_DYNAMIC_CREDS }}
|
||||
admin_username = {{ KEYSTONE_ADMIN_USER }}
|
||||
admin_password = {{ KEYSTONE_ADMIN_PASSWORD }}
|
||||
admin_project_name = {{ KEYSTONE_ADMIN_PROJECT }}
|
||||
admin_domain_name = {{ KEYSTONE_ADMIN_DOMAIN }}
|
||||
@@ -1,186 +0,0 @@
|
||||
# Introduction
|
||||
The Monasca Tempest Tests use the [OpenStack Tempest Plugin Interface](https://docs.openstack.org/tempest/latest/plugin.html). This README describes how to configure and run them using a variety of methods.
|
||||
Currently the devstack environment is needed to run the tests. Instructions on setting up a devstack environment can be found here: https://github.com/openstack/monasca-api/devstack/README.md.
|
||||
|
||||
# Configuring to run the Monasca Tempest Tests
|
||||
1. Clone the OpenStack Tempest repo, and cd to it.
|
||||
|
||||
```
|
||||
git clone https://opendev.org/openstack/tempest.git
|
||||
cd tempest
|
||||
```
|
||||
2. Create a virtualenv for running the Tempest tests and activate it. For example in the Tempest root dir
|
||||
|
||||
```
|
||||
virtualenv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
3. Install the Tempest requirements in the virtualenv.
|
||||
|
||||
```
|
||||
pip install -r requirements.txt -r test-requirements.txt
|
||||
```
|
||||
4. Create ```etc/tempest.conf``` in the Tempest root dir by running the following command:
|
||||
|
||||
```
|
||||
oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf --output-file etc/tempest.conf
|
||||
```
|
||||
|
||||
Add the following sections to ```tempest.conf``` for testing using the devstack environment.
|
||||
|
||||
```
|
||||
[identity]
|
||||
|
||||
auth_version = v3
|
||||
uri = http://127.0.0.1/identity_admin/v2.0/
|
||||
uri_v3 = http://127.0.0.1/identity_admin/v3/
|
||||
user_lockout_failure_attempts = 2
|
||||
user_locakout_duration = 5
|
||||
user_unique_last_password_count = 2
|
||||
admin_domain_scope = True
|
||||
|
||||
[auth]
|
||||
|
||||
use_dynamic_credentials = True
|
||||
admin_project_name = admin
|
||||
admin_username = admin
|
||||
admin_password = secretadmin
|
||||
admin_domain_name = Default
|
||||
```
|
||||
|
||||
Edit the variable values in the identity section to match your particular environment.
|
||||
|
||||
5. Create ```etc/logging.conf``` in the Tempest root dir by making a copying ```logging.conf.sample```.
|
||||
|
||||
6. Clone the monasca-api repo in a directory somewhere outside of the Tempest root dir.
|
||||
|
||||
7. Install the monasca-api in your venv, which will also register
|
||||
the Monasca Tempest Plugin as, monasca_tests.
|
||||
|
||||
cd into the monasca-api root directory. Making sure that the tempest virtual env is still active,
|
||||
run the following command.
|
||||
|
||||
```
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
See the [OpenStack Tempest Plugin Interface](https://docs.openstack.org/tempest/latest/plugin.html), for more details on Tempest Plugins and the plugin registration process.
|
||||
|
||||
# Running the Monasca Tempest Tests
|
||||
The Monasca Tempest Tests can be run using a variety of methods including:
|
||||
1. [Testr](https://wiki.openstack.org/wiki/Testr)
|
||||
2. [Os-testr](https://docs.openstack.org/os-testr/latest/)
|
||||
3. [PyCharm](https://www.jetbrains.com/pycharm/)
|
||||
4. Tempest Scripts in Devstack
|
||||
|
||||
## Run the tests from the CLI using testr
|
||||
|
||||
[Testr](https://wiki.openstack.org/wiki/Testr) is a test runner that can be used to run the Tempest tests.
|
||||
|
||||
1. Initializing testr is necessary to set up the .testrepository directory before using it for the first time. In the Tempest root dir:
|
||||
|
||||
```
|
||||
testr init
|
||||
```
|
||||
|
||||
2. Create a list of the Monasca Tempest Tests in a file:
|
||||
|
||||
```
|
||||
testr list-tests monasca_tempest_tests > monasca_tempest_tests
|
||||
```
|
||||
|
||||
3. Run the tests using testr:
|
||||
|
||||
```
|
||||
testr run --load-list=monasca_tempest_tests
|
||||
```
|
||||
You can also use testr to create a list of specific tests for your needs.
|
||||
|
||||
## Run the tests using Tempest Run command
|
||||
|
||||
``tempest run`` is a domain-specific command to be used as the primary
|
||||
entry point for running Tempest tests.
|
||||
|
||||
1. In the Tempest root dir:
|
||||
|
||||
```
|
||||
tempest run -r monasca_tempest_tests
|
||||
```
|
||||
|
||||
## Run the tests from the CLI using os-testr (no file necessary)
|
||||
[Os-testr](https://docs.openstack.org/os-testr/latest/) is a test wrapper that can be used to run the Monasca Tempest tests.
|
||||
|
||||
1. In the Tempest root dir:
|
||||
|
||||
```
|
||||
ostestr --serial --regex monasca_tempest_tests
|
||||
```
|
||||
```--serial``` option is necessary here. Monasca tempest tests can't be run in parallel (default option in ostestr) because some tests depend on the same data and will randomly fail.
|
||||
|
||||
## Running/Debugging the Monasca Tempest Tests in PyCharm
|
||||
|
||||
You need to install `nose` for running tests from PyCharm:
|
||||
```
|
||||
pip install nose
|
||||
```
|
||||
|
||||
Assuming that you have already created a PyCharm project for the ```monasca-api``` do the following:
|
||||
|
||||
1. In PyCharm, Edit Configurations and add a new Python tests configuration by selecting Python tests->Nosetests.
|
||||
2. Name the test. For example TestVersions.
|
||||
3. Set the path to the script with the tests to run. For example, ~/repos/monasca-api/monasca_tempest_tests/api/test_versions.py
|
||||
4. Set the name of the Class to test. For example TestVersions.
|
||||
5. Set the working directory to your local root Tempest repo. For example, ~/repos/tempest.
|
||||
6. Select the Python interpreter for your project to be the same as the one virtualenv created above. For example, ~/repos/tempest/.venv
|
||||
7. Run the tests. You should also be able to debug them.
|
||||
8. Step and repeat for other tests.
|
||||
|
||||
## Run the tests from the CLI using tempest scripts in devstack
|
||||
|
||||
1. Create a virtualenv in devstack for running the tempest tests and activate it:
|
||||
|
||||
```
|
||||
cd /opt/stack/tempest
|
||||
virtualenv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
2. Install the tempest requirements in the virtualenv:
|
||||
|
||||
```
|
||||
pip install -r requirements.txt -r test-requirements.txt
|
||||
```
|
||||
3. If you want to test changes in monasca-api code on your local machine, change directory to monasca-api and install the latest monasca-api code:
|
||||
|
||||
```
|
||||
cd /vagrant_home/<monasca-api directory>
|
||||
python setup.py install
|
||||
```
|
||||
Or if you want to use the current monasca api in devstack:
|
||||
|
||||
```
|
||||
cd /opt/stack/monasca-api
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
4. Run tempest tests:
|
||||
|
||||
```
|
||||
cd /opt/stack/tempest
|
||||
testr init
|
||||
ostestr --serial --regex monasca_tempest_tests
|
||||
```
|
||||
|
||||
# References
|
||||
This section provides a few additional references that might be useful:
|
||||
* [Tempest - The OpenStack Integration Test Suite](https://docs.openstack.org/tempest/latest/overview.html#quickstart)
|
||||
* [Tempest Configuration Guide](https://github.com/openstack/tempest/blob/master/doc/source/configuration.rst#id1)
|
||||
* [OpenStack Tempest Plugin Interface](https://docs.openstack.org/tempest/latest/plugin.html)
|
||||
|
||||
In addition to the above references, another source of information is the following OpenStack projects:
|
||||
* [Manila Tempest Tests](https://github.com/openstack/manila/tree/master/manila_tempest_tests)
|
||||
* [Congress Tempest Tests](https://github.com/openstack/congress/tree/master/congress_tempest_tests).
|
||||
In particular, the Manila Tempest Tests were used as a reference implementation to develop the Monasca Tempest Tests. There is also a wiki [HOWTO use tempest with manila](https://wiki.openstack.org/wiki/Manila/docs/HOWTO_use_tempest_with_manila) that might be useful for Monasca too.
|
||||
|
||||
# Issues
|
||||
* Update documentation for testing using Devstack when available.
|
||||
* Consider changing from monasca_tempest_tests to monasca_api_tempest_tests.
|
||||
@@ -1,23 +0,0 @@
|
||||
# (C) Copyright 2015,2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 import clients
|
||||
|
||||
from monasca_tempest_tests.services import monasca_client
|
||||
|
||||
|
||||
class Manager(clients.Manager):
|
||||
def __init__(self, credentials=None):
|
||||
super(Manager, self).__init__(credentials)
|
||||
self.monasca_client = monasca_client.MonascaClient(self.auth_provider)
|
||||
@@ -1,35 +0,0 @@
|
||||
# Copyright 2019 FUJITSU LIMITED
|
||||
#
|
||||
# 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 import clients
|
||||
|
||||
from monasca_tempest_tests.services import elasticsearch_client
|
||||
from monasca_tempest_tests.services import events_api_client
|
||||
|
||||
|
||||
class Manager(clients.Manager):
|
||||
def __init__(self, credentials=None):
|
||||
super(Manager, self).__init__(credentials)
|
||||
|
||||
self.events_api_client = events_api_client.EventApiClient(
|
||||
self.auth_provider,
|
||||
'events',
|
||||
None
|
||||
)
|
||||
|
||||
self.events_search_client = elasticsearch_client.ElasticsearchClient(
|
||||
self.auth_provider,
|
||||
'events-search',
|
||||
None
|
||||
)
|
||||
@@ -1,38 +0,0 @@
|
||||
# Copyright 2015-2016 FUJITSU LIMITED
|
||||
#
|
||||
# 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 import clients
|
||||
from tempest import config
|
||||
|
||||
from monasca_tempest_tests.services import elasticsearch_client
|
||||
from monasca_tempest_tests.services import log_api_v3_client
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class Manager(clients.Manager):
|
||||
def __init__(self, credentials=None):
|
||||
super(Manager, self).__init__(credentials)
|
||||
|
||||
self.log_api_client = log_api_v3_client.LogApiV3Client(
|
||||
self.auth_provider,
|
||||
CONF.monitoring.catalog_type_logs,
|
||||
None
|
||||
)
|
||||
|
||||
self.log_search_client = elasticsearch_client.ElasticsearchClient(
|
||||
self.auth_provider,
|
||||
CONF.monitoring.catalog_type_log_query,
|
||||
None
|
||||
)
|
||||
@@ -1,79 +0,0 @@
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
service_available_group = cfg.OptGroup(name='service_available',
|
||||
title='Available OpenStack Services')
|
||||
ServiceAvailableGroup = [
|
||||
cfg.BoolOpt('logs',
|
||||
default=True,
|
||||
help=('Whether or not Monasca-Log-Api '
|
||||
'is expected to be available')),
|
||||
cfg.BoolOpt('logs-search',
|
||||
default=True,
|
||||
help=('Whether or not Monasca-Log-Api search engine '
|
||||
'(ElasticSearch) is expected to be available')),
|
||||
cfg.BoolOpt("monasca",
|
||||
default=True,
|
||||
help="Whether or not Monasca is expected to be "
|
||||
"available")]
|
||||
|
||||
monitoring_group = cfg.OptGroup(name="monitoring",
|
||||
title="Monitoring Service Options")
|
||||
|
||||
MonitoringGroup = [
|
||||
cfg.StrOpt("region",
|
||||
default="",
|
||||
help="The monitoring region name to use. If empty, the value "
|
||||
"of identity.region is used instead. If no such region "
|
||||
"is found in the service catalog, the first found one is "
|
||||
"used."),
|
||||
cfg.StrOpt("catalog_type",
|
||||
default="monitoring",
|
||||
help="Keystone catalog service type of the monitoring service."),
|
||||
cfg.StrOpt('catalog_type_logs',
|
||||
default='logs',
|
||||
help='Keystone catalog service type of the logging service.'),
|
||||
cfg.StrOpt('catalog_type_log_query',
|
||||
default='logs-search',
|
||||
help='Keystone catalog service type of the log query service.'),
|
||||
cfg.StrOpt('log_query_message_field',
|
||||
default='message',
|
||||
help='The field under which the log message is stored.'),
|
||||
cfg.StrOpt('log_uri_path',
|
||||
default='/logs',
|
||||
help='Path used to form Log API URI.'),
|
||||
cfg.ListOpt('log_project_id_path',
|
||||
default=['_source', 'tenant'],
|
||||
help='Series of keys to access the Project ID field in a persisted'
|
||||
'log file.'),
|
||||
cfg.StrOpt('endpoint_type',
|
||||
default='publicURL',
|
||||
choices=['public', 'admin', 'internal',
|
||||
'publicURL', 'adminURL', 'internalURL'],
|
||||
help="The endpoint type to use for the monitoring service."),
|
||||
cfg.StrOpt('api_version',
|
||||
default='v2.0',
|
||||
help='monasca-log-api API version'),
|
||||
cfg.StrOpt('kibana_version',
|
||||
default='7.3.0',
|
||||
help='Kibana version'),
|
||||
cfg.IntOpt('log_api_max_log_size',
|
||||
default=1024 * 1024,
|
||||
help=('Refers to payload/envelope size. This should be set '
|
||||
'to the same value as "[service]max_log_size" in the '
|
||||
'monasca-log-api configuration'))
|
||||
]
|
||||
@@ -1,102 +0,0 @@
|
||||
#
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
# (C) Copyright 2017 FUJITSU LIMITED
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
sleep 6
|
||||
|
||||
function load_devstack_utilities {
|
||||
source $BASE/new/devstack/stackrc
|
||||
source $BASE/new/devstack/functions
|
||||
source $BASE/new/devstack/openrc admin admin
|
||||
}
|
||||
|
||||
function setup_monasca_api {
|
||||
|
||||
local constraints="-c /opt/stack/new/requirements/upper-constraints.txt"
|
||||
|
||||
pushd $TEMPEST_DIR
|
||||
sudo -EH pip install $constraints -r requirements.txt -r test-requirements.txt
|
||||
popd;
|
||||
|
||||
pushd $MONASCA_API_DIR
|
||||
sudo -EH pip install $constraints -r requirements.txt -r test-requirements.txt
|
||||
sudo -EH python setup.py install
|
||||
popd;
|
||||
}
|
||||
|
||||
function set_tempest_conf {
|
||||
|
||||
local conf_file=$TEMPEST_DIR/etc/tempest.conf
|
||||
pushd $TEMPEST_DIR
|
||||
oslo-config-generator \
|
||||
--config-file tempest/cmd/config-generator.tempest.conf \
|
||||
--output-file $conf_file
|
||||
popd
|
||||
|
||||
cp -f $DEST/tempest/etc/logging.conf.sample $DEST/tempest/etc/logging.conf
|
||||
|
||||
# set identity section
|
||||
iniset $conf_file identity admin_domain_scope True
|
||||
iniset $conf_file identity user_unique_last_password_count 2
|
||||
iniset $conf_file identity user_locakout_duration 5
|
||||
iniset $conf_file identity user_lockout_failure_attempts 2
|
||||
iniset $conf_file identity uri $OS_AUTH_URL/v2.0
|
||||
iniset $conf_file identity uri_v3 $OS_AUTH_URL/v3
|
||||
iniset $conf_file identity auth_version v$OS_IDENTITY_API_VERSION
|
||||
# set auth section
|
||||
iniset $conf_file auth use_dynamic_credentials True
|
||||
iniset $conf_file auth admin_username $OS_USERNAME
|
||||
iniset $conf_file auth admin_password $OS_PASSWORD
|
||||
iniset $conf_file auth admin_domain_name $OS_PROJECT_DOMAIN_ID
|
||||
iniset $conf_file auth admin_project_name $OS_PROJECT_NAME
|
||||
|
||||
}
|
||||
|
||||
function function_exists {
|
||||
declare -f -F $1 > /dev/null
|
||||
}
|
||||
|
||||
if ! function_exists echo_summary; then
|
||||
function echo_summary {
|
||||
echo $@
|
||||
}
|
||||
fi
|
||||
|
||||
XTRACE=$(set +o | grep xtrace)
|
||||
set -o xtrace
|
||||
|
||||
echo_summary "monasca's post_test_hook.sh was called..."
|
||||
(set -o posix; set)
|
||||
|
||||
# save ref to monasca-api dir
|
||||
export MONASCA_API_DIR="$BASE/new/monasca-api"
|
||||
export TEMPEST_DIR="$BASE/new/tempest"
|
||||
|
||||
sudo chown -R $USER:stack $MONASCA_API_DIR
|
||||
sudo chown -R $USER:stack $TEMPEST_DIR
|
||||
|
||||
load_devstack_utilities
|
||||
setup_monasca_api
|
||||
set_tempest_conf
|
||||
|
||||
(cd $TEMPEST_DIR; testr init)
|
||||
(cd $TEMPEST_DIR; testr list-tests monasca_tempest_tests > monasca_tempest_tests)
|
||||
(cd $TEMPEST_DIR; cat monasca_tempest_tests)
|
||||
(cd $TEMPEST_DIR; cat monasca_tempest_tests | grep gate > monasca_tempest_tests_gate)
|
||||
(cd $TEMPEST_DIR; testr run --subunit --load-list=monasca_tempest_tests_gate | subunit-trace --fails)
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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 monasca_tempest_tests import config as config_monitoring
|
||||
|
||||
|
||||
class MonascaTempestPlugin(plugins.TempestPlugin):
|
||||
def load_tests(self):
|
||||
base_path = os.path.split(os.path.dirname(
|
||||
os.path.abspath(__file__)))[0]
|
||||
test_dir = "monasca_tempest_tests/tests"
|
||||
full_test_dir = os.path.join(base_path, test_dir)
|
||||
return full_test_dir, base_path
|
||||
|
||||
def register_opts(self, conf):
|
||||
config.register_opt_group(
|
||||
conf,
|
||||
config_monitoring.service_available_group,
|
||||
config_monitoring.ServiceAvailableGroup
|
||||
)
|
||||
config.register_opt_group(conf,
|
||||
config_monitoring.monitoring_group,
|
||||
config_monitoring.MonitoringGroup)
|
||||
|
||||
def get_opt_lists(self):
|
||||
return [(config_monitoring.monitoring_group.name,
|
||||
config_monitoring.MonitoringGroup)]
|
||||
@@ -1,91 +0,0 @@
|
||||
# Copyright 2015 FUJITSU LIMITED
|
||||
#
|
||||
# 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_serialization import jsonutils as json
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
header = {'kbn-version': CONF.monitoring.kibana_version, 'kbn-xsrf': 'kibana'}
|
||||
|
||||
|
||||
class ElasticsearchClient(rest_client.RestClient):
|
||||
uri_prefix = "/elasticsearch"
|
||||
|
||||
def __init__(self, auth_provider, service, region):
|
||||
super(ElasticsearchClient, self).__init__(
|
||||
auth_provider,
|
||||
service,
|
||||
region,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def deserialize(body):
|
||||
body = body.decode('utf-8')
|
||||
return json.loads(body.replace("\n", ""))
|
||||
|
||||
@staticmethod
|
||||
def serialize(body):
|
||||
return json.dumps(body)
|
||||
|
||||
def get_metadata(self):
|
||||
uri = "/"
|
||||
|
||||
response, body = self.get(self._uri(uri))
|
||||
self.expected_success(200, response.status)
|
||||
|
||||
if body:
|
||||
body = self.deserialize(body)
|
||||
return response, body
|
||||
|
||||
def count_search_messages(self, message, headers):
|
||||
return len(self.search_messages(message, headers))
|
||||
|
||||
def search_messages(self, message, headers=None):
|
||||
uri = '_msearch'
|
||||
field = CONF.monitoring.log_query_message_field
|
||||
body = ('\n'
|
||||
'{"index" : "*", "search_type" : "dfs_query_then_fetch"}\n'
|
||||
'{"query" : {"match" : {"' + field + '":"' + message + '"}}}'
|
||||
'\n')
|
||||
response, body = self.post(self._uri(uri), body, headers)
|
||||
self.expected_success(200, response.status)
|
||||
body = self.deserialize(body)
|
||||
return body['responses'][0].get('hits', {}).get('hits', [])
|
||||
|
||||
def search_event_by_event_type(self, event_type):
|
||||
uri = '_msearch'
|
||||
body = u"""
|
||||
{"index" : "*", "search_type" : "dfs_query_then_fetch"}
|
||||
{"query" : {"match" : {"event_type":" """ + event_type + """ "}}}\n"""
|
||||
response, body = self.post(self._uri(uri), body, header)
|
||||
self.expected_success(200, response.status)
|
||||
body = self.deserialize(body)
|
||||
return body['responses'][0].get('hits', {}).get('hits', [])
|
||||
|
||||
def search_event(self, event):
|
||||
uri = '_msearch'
|
||||
event = json.dumps(event)
|
||||
body = u"""
|
||||
{"index" : "*", "search_type" : "dfs_query_then_fetch"}
|
||||
{"query" : {"match" : {"event":" """ + event + """ "}}}\n"""
|
||||
response, body = self.post(self._uri(uri), body, header)
|
||||
self.expected_success(200, response.status)
|
||||
body = self.deserialize(body)
|
||||
return body['responses'][0].get('hits', {}).get('hits', [])
|
||||
|
||||
def _uri(self, url):
|
||||
return '{}/{}'.format(self.uri_prefix, url)
|
||||
@@ -1,34 +0,0 @@
|
||||
# Copyright 2019 FUJITSU LIMITED
|
||||
#
|
||||
# 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 json
|
||||
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class EventApiClient(rest_client.RestClient):
|
||||
|
||||
_uri = '/v1.0/events'
|
||||
|
||||
def __init__(self, auth_provider, service, region):
|
||||
super(EventApiClient, self).__init__(
|
||||
auth_provider,
|
||||
service,
|
||||
region
|
||||
)
|
||||
|
||||
def send_events(self, events, headers=None):
|
||||
msg = json.dumps(events)
|
||||
resp, body = self.post(self._uri, body=msg, headers=headers)
|
||||
return resp, body
|
||||
@@ -1,54 +0,0 @@
|
||||
# Copyright 2015 FUJITSU LIMITED
|
||||
#
|
||||
# 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 urllib.parse import urlencode
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
from tempest import config
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class LogApiV3Client(rest_client.RestClient):
|
||||
|
||||
def __init__(self, auth_provider, service, region):
|
||||
super(LogApiV3Client, self).__init__(
|
||||
auth_provider,
|
||||
service,
|
||||
region
|
||||
)
|
||||
|
||||
def get_version(self):
|
||||
resp, response_body = self.send_request('GET', '/')
|
||||
return resp, response_body
|
||||
|
||||
def send_single_log(self, log, headers=None, fields=None):
|
||||
default_headers = {
|
||||
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
|
||||
'X-Roles': 'admin',
|
||||
}
|
||||
default_headers.update(headers)
|
||||
msg = json.dumps(log)
|
||||
uri = CONF.monitoring.log_uri_path
|
||||
|
||||
if fields:
|
||||
uri += '?' + urlencode(fields)
|
||||
|
||||
resp, body = self.post(uri, msg, default_headers)
|
||||
|
||||
return resp, body
|
||||
|
||||
def custom_request(self, method, headers=None, body=None):
|
||||
self.request(method=method, url=CONF.monitoring.log_uri_path, headers=headers, body=body)
|
||||
@@ -1,346 +0,0 @@
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 import config
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class MonascaClient(rest_client.RestClient):
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(MonascaClient, self).__init__(
|
||||
auth_provider,
|
||||
CONF.monitoring.catalog_type,
|
||||
CONF.monitoring.region or CONF.identity.region,
|
||||
endpoint_type=CONF.monitoring.endpoint_type)
|
||||
|
||||
def get_version(self):
|
||||
resp, response_body = self.get('')
|
||||
return resp, response_body
|
||||
|
||||
def create_metrics(self, metrics, tenant_id=None):
|
||||
uri = 'metrics'
|
||||
if tenant_id:
|
||||
uri = uri + '?tenant_id=%s' % tenant_id
|
||||
request_body = json.dumps(metrics)
|
||||
resp, response_body = self.post(uri, request_body)
|
||||
return resp, response_body
|
||||
|
||||
def list_metrics(self, query_params=None):
|
||||
uri = 'metrics'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_metrics_names(self, query_params=None):
|
||||
uri = 'metrics/names'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_dimension_names(self, query_params=None):
|
||||
uri = 'metrics/dimensions/names'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_dimension_values(self, query_params=None):
|
||||
uri = 'metrics/dimensions/names/values'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_measurements(self, query_params=None):
|
||||
uri = 'metrics/measurements'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_statistics(self, query_params=None):
|
||||
uri = 'metrics/statistics'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def create_notifications(self, notification):
|
||||
uri = 'notification-methods'
|
||||
request_body = json.dumps(notification)
|
||||
resp, response_body = self.post(uri, request_body)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def create_notification_method(self,
|
||||
name=None,
|
||||
type=None,
|
||||
address=None,
|
||||
period=None):
|
||||
uri = 'notification-methods'
|
||||
request_body = {}
|
||||
if name is not None:
|
||||
request_body['name'] = name
|
||||
if type is not None:
|
||||
request_body['type'] = type
|
||||
if address is not None:
|
||||
request_body['address'] = address
|
||||
if period is not None:
|
||||
request_body['period'] = period
|
||||
resp, response_body = self.post(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def delete_notification_method(self, id):
|
||||
uri = 'notification-methods/' + id
|
||||
resp, response_body = self.delete(uri)
|
||||
return resp, response_body
|
||||
|
||||
def get_notification_method(self, id):
|
||||
uri = 'notification-methods/' + id
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_notification_methods(self, query_params=None):
|
||||
uri = 'notification-methods'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def update_notification_method(self, id, name, type, address, period=None):
|
||||
uri = 'notification-methods/' + id
|
||||
request_body = {}
|
||||
request_body['name'] = name
|
||||
request_body['type'] = type
|
||||
request_body['address'] = address
|
||||
if period is not None:
|
||||
request_body['period'] = period
|
||||
resp, response_body = self.put(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def patch_notification_method(self, id,
|
||||
name=None, type=None,
|
||||
address=None, period=None):
|
||||
uri = 'notification-methods/' + id
|
||||
request_body = {}
|
||||
if name is not None:
|
||||
request_body['name'] = name
|
||||
if type is not None:
|
||||
request_body['type'] = type
|
||||
if address is not None:
|
||||
request_body['address'] = address
|
||||
if period is not None:
|
||||
request_body['period'] = period
|
||||
resp, response_body = self.patch(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_notification_method_types(self, query_params=None):
|
||||
uri = 'notification-methods/types'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def create_alarm_definitions(self, alarm_definitions):
|
||||
uri = 'alarm-definitions'
|
||||
request_body = json.dumps(alarm_definitions)
|
||||
resp, response_body = self.post(uri, request_body)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_alarm_definitions(self, query_params=None):
|
||||
uri = 'alarm-definitions'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def get_alarm_definition(self, id):
|
||||
uri = 'alarm-definitions/' + id
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def delete_alarm_definition(self, id):
|
||||
uri = 'alarm-definitions/' + id
|
||||
resp, response_body = self.delete(uri)
|
||||
return resp, response_body
|
||||
|
||||
def update_alarm_definition(self, id, name, expression, description,
|
||||
actions_enabled, match_by,
|
||||
severity, alarm_actions,
|
||||
ok_actions, undetermined_actions,
|
||||
**kwargs):
|
||||
uri = 'alarm-definitions/' + id
|
||||
request_body = {}
|
||||
request_body['name'] = name
|
||||
request_body['expression'] = expression
|
||||
request_body['description'] = description
|
||||
request_body['actions_enabled'] = actions_enabled
|
||||
request_body['match_by'] = match_by
|
||||
request_body['severity'] = severity
|
||||
request_body['alarm_actions'] = alarm_actions
|
||||
request_body['ok_actions'] = ok_actions
|
||||
request_body['undetermined_actions'] = undetermined_actions
|
||||
|
||||
for key, value in kwargs.items():
|
||||
request_body[key] = value
|
||||
|
||||
resp, response_body = self.put(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def patch_alarm_definition(self,
|
||||
id,
|
||||
name=None,
|
||||
description=None,
|
||||
expression=None,
|
||||
actions_enabled=None,
|
||||
match_by=None,
|
||||
severity=None,
|
||||
alarm_actions=None,
|
||||
ok_actions=None,
|
||||
undetermined_actions=None,
|
||||
**kwargs):
|
||||
uri = 'alarm-definitions/' + id
|
||||
request_body = {}
|
||||
if name is not None:
|
||||
request_body['name'] = name
|
||||
if description is not None:
|
||||
request_body['description'] = description
|
||||
if expression is not None:
|
||||
request_body['expression'] = expression
|
||||
if actions_enabled is not None:
|
||||
request_body['actions_enabled'] = actions_enabled
|
||||
if match_by is not None:
|
||||
request_body['match_by'] = match_by
|
||||
if severity is not None:
|
||||
request_body['severity'] = severity
|
||||
if alarm_actions is not None:
|
||||
request_body['alarm_actions'] = alarm_actions
|
||||
if ok_actions is not None:
|
||||
request_body['ok_actions'] = ok_actions
|
||||
if undetermined_actions is not None:
|
||||
request_body['undetermined_actions'] = undetermined_actions
|
||||
|
||||
for key, value in kwargs.items():
|
||||
request_body[key] = value
|
||||
|
||||
resp, response_body = self.patch(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_alarms(self, query_params=None):
|
||||
uri = 'alarms'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def get_alarm(self, id):
|
||||
uri = 'alarms/' + id
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def delete_alarm(self, id):
|
||||
uri = 'alarms/' + id
|
||||
resp, response_body = self.delete(uri)
|
||||
return resp, response_body
|
||||
|
||||
def update_alarm(self, id, state, lifecycle_state, link, **kwargs):
|
||||
uri = 'alarms/' + id
|
||||
request_body = {}
|
||||
request_body['state'] = state
|
||||
request_body['lifecycle_state'] = lifecycle_state
|
||||
request_body['link'] = link
|
||||
|
||||
for key, value in kwargs.items():
|
||||
request_body[key] = value
|
||||
|
||||
resp, response_body = self.put(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def patch_alarm(self, id, state=None, lifecycle_state=None, link=None,
|
||||
**kwargs):
|
||||
uri = 'alarms/' + id
|
||||
request_body = {}
|
||||
if state is not None:
|
||||
request_body['state'] = state
|
||||
if lifecycle_state is not None:
|
||||
request_body['lifecycle_state'] = lifecycle_state
|
||||
if link is not None:
|
||||
request_body['link'] = link
|
||||
|
||||
for key, value in kwargs.items():
|
||||
request_body[key] = value
|
||||
|
||||
resp, response_body = self.patch(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def count_alarms(self, query_params=None):
|
||||
uri = 'alarms/count'
|
||||
if query_params is not None:
|
||||
uri += query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_alarms_state_history(self, query_params=None):
|
||||
uri = 'alarms/state-history'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def list_alarm_state_history(self, id, query_params=None):
|
||||
uri = 'alarms/' + id + '/state-history'
|
||||
if query_params is not None:
|
||||
uri = uri + query_params
|
||||
resp, response_body = self.get(uri)
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
# For Negative Tests
|
||||
def update_alarm_definition_with_no_ok_actions(self, id, name,
|
||||
expression, description,
|
||||
actions_enabled, match_by,
|
||||
severity, alarm_actions,
|
||||
undetermined_actions,
|
||||
**kwargs):
|
||||
uri = 'alarm-definitions/' + id
|
||||
request_body = {}
|
||||
request_body['name'] = name
|
||||
request_body['expression'] = expression
|
||||
request_body['description'] = description
|
||||
request_body['actions_enabled'] = actions_enabled
|
||||
request_body['match_by'] = match_by
|
||||
request_body['severity'] = severity
|
||||
request_body['alarm_actions'] = alarm_actions
|
||||
request_body['undetermined_actions'] = undetermined_actions
|
||||
|
||||
for key, value in kwargs.items():
|
||||
request_body[key] = value
|
||||
|
||||
resp, response_body = self.put(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
|
||||
def update_notification_method_with_no_address(self, id, name, type,
|
||||
period=None):
|
||||
uri = 'notification-methods/' + id
|
||||
request_body = {}
|
||||
request_body['name'] = name
|
||||
request_body['type'] = type
|
||||
if period is not None:
|
||||
request_body['period'] = period
|
||||
resp, response_body = self.put(uri, json.dumps(request_body))
|
||||
return resp, json.loads(response_body)
|
||||
@@ -1,100 +0,0 @@
|
||||
# (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 urllib.parse as urlparse
|
||||
|
||||
from tempest.common import credentials_factory
|
||||
from tempest import config
|
||||
from tempest.lib import exceptions
|
||||
import tempest.test
|
||||
|
||||
from monasca_tempest_tests.clients import api as clients
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BaseMonascaTest(tempest.test.BaseTestCase):
|
||||
"""Base test case class for all Monasca API tests."""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseMonascaTest, cls).skip_checks()
|
||||
if not CONF.service_available.monasca:
|
||||
raise cls.skipException("Monasca support is required")
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseMonascaTest, cls).resource_setup()
|
||||
auth_version = CONF.identity.auth_version
|
||||
cls.cred_provider = credentials_factory.get_credentials_provider(
|
||||
cls.__name__,
|
||||
force_tenant_isolation=True,
|
||||
identity_version=auth_version)
|
||||
credentials = cls.cred_provider.get_creds_by_roles(
|
||||
['monasca-user', 'monasca-read-only-user', 'admin']).credentials
|
||||
cls.os = clients.Manager(credentials=credentials)
|
||||
cls.monasca_client = cls.os.monasca_client
|
||||
cls.projects_client = cls.os.projects_client
|
||||
|
||||
@staticmethod
|
||||
def cleanup_resources(method, list_of_ids):
|
||||
for resource_id in list_of_ids:
|
||||
try:
|
||||
method(resource_id)
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(BaseMonascaTest, cls).resource_cleanup()
|
||||
resp, response_body = cls.monasca_client.list_alarm_definitions()
|
||||
if resp.status == 200:
|
||||
if 'elements' in response_body:
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
id = element['id']
|
||||
cls.monasca_client.delete_alarm_definition(id)
|
||||
|
||||
resp, response_body = cls.monasca_client.list_notification_methods()
|
||||
if resp.status == 200:
|
||||
if 'elements' in response_body:
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
id = element['id']
|
||||
cls.monasca_client.delete_notification_method(id)
|
||||
|
||||
resp, response_body = cls.monasca_client.list_alarms()
|
||||
if resp.status == 200:
|
||||
if 'elements' in response_body:
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
id = element['id']
|
||||
cls.monasca_client.delete_alarm(id)
|
||||
cls.cred_provider.clear_creds()
|
||||
|
||||
def _get_offset(self, response_body):
|
||||
next_link = None
|
||||
self_link = None
|
||||
for link in response_body['links']:
|
||||
if link['rel'] == 'next':
|
||||
next_link = link['href']
|
||||
if link['rel'] == 'self':
|
||||
self_link = link['href']
|
||||
if not next_link:
|
||||
query_parms = urlparse.parse_qs(urlparse.urlparse(self_link).query)
|
||||
self.fail("No next link returned with query parameters: {}".format(query_parms))
|
||||
query_params = urlparse.parse_qs(urlparse.urlparse(next_link).query)
|
||||
if 'offset' not in query_params:
|
||||
self.fail("No offset in next link: {}".format(next_link))
|
||||
return query_params['offset'][0]
|
||||
@@ -1,47 +0,0 @@
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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.
|
||||
|
||||
MAX_RETRIES = 60
|
||||
RETRY_WAIT_SECS = 1
|
||||
ONE_MINUTE_TIME_OUT = 60
|
||||
|
||||
ALARM_DEFINITION_CREATION_WAIT = 3
|
||||
|
||||
MAX_METRIC_NAME_LENGTH = 255
|
||||
MAX_DIMENSION_KEY_LENGTH = 255
|
||||
MAX_DIMENSION_VALUE_LENGTH = 255
|
||||
INVALID_DIMENSION_CHARS = "<>={},\"\\;&"
|
||||
INVALID_NAME_CHARS = INVALID_DIMENSION_CHARS + "()"
|
||||
|
||||
MAX_ALARM_DEFINITION_NAME_LENGTH = 255
|
||||
MAX_ALARM_DEFINITION_DESCRIPTION_LENGTH = 255
|
||||
MAX_ALARM_DEFINITION_ACTIONS_LENGTH = 50
|
||||
|
||||
MAX_NOTIFICATION_METHOD_NAME_LENGTH = 250
|
||||
MAX_NOTIFICATION_METHOD_TYPE_LENGTH = 100
|
||||
MAX_NOTIFICATION_METHOD_ADDRESS_LENGTH = 512
|
||||
INVALID_CHARS_NOTIFICATION = "<>={}(),\"\\;&"
|
||||
|
||||
MAX_LIST_MEASUREMENTS_NAME_LENGTH = 255
|
||||
|
||||
MAX_LIST_STATISTICS_NAME_LENGTH = 255
|
||||
|
||||
MAX_ALARM_LIFECYCLE_STATE_LENGTH = 50
|
||||
MAX_ALARM_METRIC_NAME_LENGTH = 255
|
||||
MAX_ALARM_METRIC_DIMENSIONS_KEY_LENGTH = 255
|
||||
MAX_ALARM_METRIC_DIMENSIONS_VALUE_LENGTH = 255
|
||||
MAX_ALARM_LINK_LENGTH = 512
|
||||
|
||||
MAX_VALUE_META_NAME_LENGTH = 255
|
||||
MAX_VALUE_META_TOTAL_LENGTH = 2048
|
||||
@@ -1,179 +0,0 @@
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
# (C) Copyright 2017 SUSE LLC
|
||||
#
|
||||
# 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 datetime
|
||||
import time
|
||||
import urllib.parse as urlparse
|
||||
|
||||
from tempest.lib.common.utils import data_utils
|
||||
|
||||
NUM_ALARM_DEFINITIONS = 2
|
||||
NUM_MEASUREMENTS = 100
|
||||
|
||||
|
||||
def create_metric(name='name-1',
|
||||
dimensions={
|
||||
'key-1': 'value-1',
|
||||
'key-2': 'value-2'
|
||||
},
|
||||
timestamp=None,
|
||||
value=0.0,
|
||||
value_meta={
|
||||
'key-1': 'value-1',
|
||||
'key-2': 'value-2'
|
||||
},
|
||||
):
|
||||
metric = {}
|
||||
if name is not None:
|
||||
metric['name'] = name
|
||||
if dimensions is not None:
|
||||
metric['dimensions'] = dimensions
|
||||
if timestamp is not None:
|
||||
metric['timestamp'] = timestamp
|
||||
else:
|
||||
metric['timestamp'] = int(time.time() * 1000)
|
||||
if value is not None:
|
||||
metric['value'] = value
|
||||
if value_meta is not None:
|
||||
metric['value_meta'] = value_meta
|
||||
return metric
|
||||
|
||||
|
||||
def create_notification(name=data_utils.rand_name('notification-'),
|
||||
type='EMAIL',
|
||||
address='john.doe@domain.com',
|
||||
period=0):
|
||||
notification = {}
|
||||
if name is not None:
|
||||
notification['name'] = name
|
||||
if type is not None:
|
||||
notification['type'] = type
|
||||
if address is not None:
|
||||
notification['address'] = address
|
||||
if period is not None:
|
||||
notification['period'] = period
|
||||
return notification
|
||||
|
||||
|
||||
def create_alarm_definition(name=None,
|
||||
description=None,
|
||||
expression=None,
|
||||
match_by=None,
|
||||
severity=None,
|
||||
alarm_actions=None,
|
||||
ok_actions=None,
|
||||
undetermined_actions=None):
|
||||
alarm_definition = {}
|
||||
if name is not None:
|
||||
alarm_definition['name'] = name
|
||||
if description is not None:
|
||||
alarm_definition['description'] = description
|
||||
if expression is not None:
|
||||
alarm_definition['expression'] = expression
|
||||
if match_by is not None:
|
||||
alarm_definition['match_by'] = match_by
|
||||
if severity is not None:
|
||||
alarm_definition['severity'] = severity
|
||||
if alarm_actions is not None:
|
||||
alarm_definition['alarm_actions'] = alarm_actions
|
||||
if ok_actions is not None:
|
||||
alarm_definition['ok_actions'] = ok_actions
|
||||
if undetermined_actions is not None:
|
||||
alarm_definition['undetermined_actions'] = undetermined_actions
|
||||
return alarm_definition
|
||||
|
||||
|
||||
def delete_alarm_definitions(monasca_client):
|
||||
# Delete alarm definitions
|
||||
resp, response_body = monasca_client.list_alarm_definitions()
|
||||
elements = response_body['elements']
|
||||
if elements:
|
||||
for element in elements:
|
||||
alarm_def_id = element['id']
|
||||
monasca_client.delete_alarm_definition(alarm_def_id)
|
||||
|
||||
|
||||
def timestamp_to_iso(timestamp):
|
||||
time_utc = datetime.datetime.utcfromtimestamp(timestamp / 1000.0)
|
||||
time_iso_base = time_utc.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
time_iso_base += 'Z'
|
||||
return time_iso_base
|
||||
|
||||
|
||||
def timestamp_to_iso_millis(timestamp):
|
||||
time_utc = datetime.datetime.utcfromtimestamp(timestamp / 1000.0)
|
||||
time_iso_base = time_utc.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
time_iso_microsecond = time_utc.strftime(".%f")
|
||||
time_iso_millisecond = time_iso_base + time_iso_microsecond[0:4] + 'Z'
|
||||
return time_iso_millisecond
|
||||
|
||||
|
||||
def get_query_param(uri, query_param_name):
|
||||
query_param_val = None
|
||||
parsed_uri = urlparse.urlparse(uri)
|
||||
for query_param in parsed_uri.query.split('&'):
|
||||
parsed_query_name, parsed_query_val = query_param.split('=', 1)
|
||||
if query_param_name == parsed_query_name:
|
||||
query_param_val = parsed_query_val
|
||||
return query_param_val
|
||||
|
||||
|
||||
def get_expected_elements_inner_offset_limit(all_elements, offset, limit, inner_key):
|
||||
expected_elements = []
|
||||
total_statistics = 0
|
||||
|
||||
if offset is None:
|
||||
offset_id = None
|
||||
offset_time = ""
|
||||
passed_offset = True
|
||||
else:
|
||||
offset_tuple = offset.split('_')
|
||||
offset_id = offset_tuple[0] if len(offset_tuple) > 1 else u'0'
|
||||
offset_time = offset_tuple[1] if len(offset_tuple) > 1 else offset_tuple[0]
|
||||
passed_offset = False
|
||||
|
||||
for element in all_elements:
|
||||
element_id = element['id']
|
||||
if (not passed_offset) and element_id != offset_id:
|
||||
continue
|
||||
next_element = None
|
||||
|
||||
for value in element[inner_key]:
|
||||
if passed_offset or (element_id == offset_id and value[0] > offset_time):
|
||||
if not passed_offset:
|
||||
passed_offset = True
|
||||
if not next_element:
|
||||
next_element = element.copy()
|
||||
next_element[inner_key] = [value]
|
||||
else:
|
||||
next_element[inner_key].append(value)
|
||||
total_statistics += 1
|
||||
if total_statistics >= limit:
|
||||
break
|
||||
|
||||
if next_element:
|
||||
expected_elements.append(next_element)
|
||||
|
||||
if total_statistics >= limit:
|
||||
break
|
||||
|
||||
if element_id == offset_id:
|
||||
passed_offset = True
|
||||
|
||||
# if index is used in the element id, reset to start at zero
|
||||
if expected_elements and expected_elements[0]['id'].isdigit():
|
||||
for i in range(len(expected_elements)):
|
||||
expected_elements[i]['id'] = str(i)
|
||||
|
||||
return expected_elements
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,141 +0,0 @@
|
||||
# (C) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
MIN_HISTORY = 2
|
||||
|
||||
|
||||
class TestAlarmStateHistoryMultipleTransitions(base.BaseMonascaTest):
|
||||
# For testing list alarm state history with the same alarm ID, two alarm
|
||||
# transitions are needed. One transit from ALARM state to UNDETERMINED
|
||||
# state and the other one from UNDETERMINED state to ALARM state.
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestAlarmStateHistoryMultipleTransitions, cls).resource_setup()
|
||||
alarm_definition = helpers.create_alarm_definition(
|
||||
name=data_utils.rand_name('alarm_state_history'),
|
||||
expression="min(name-1) < 1.0")
|
||||
cls.monasca_client.create_alarm_definitions(alarm_definition)
|
||||
for timer in range(constants.MAX_RETRIES):
|
||||
# create some metrics to prime the system and create
|
||||
# MIN_HISTORY alarms
|
||||
metric = helpers.create_metric(
|
||||
name="name-1", dimensions={'key1': 'value1'}, value=0.0)
|
||||
cls.monasca_client.create_metrics(metric)
|
||||
# sleep 1 second between metrics to make sure timestamps
|
||||
# are different in the second field. Influxdb has a bug
|
||||
# where it does not sort properly by milliseconds. .014
|
||||
# is sorted as greater than .138
|
||||
time.sleep(1.0)
|
||||
resp, response_body = cls.monasca_client.\
|
||||
list_alarms_state_history()
|
||||
elements = response_body['elements']
|
||||
if len(elements) >= 1:
|
||||
break
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
|
||||
time.sleep(constants.MAX_RETRIES)
|
||||
|
||||
for timer in range(constants.MAX_RETRIES * 2):
|
||||
metric = helpers.create_metric(
|
||||
name="name-1", dimensions={'key2': 'value2'}, value=2.0)
|
||||
cls.monasca_client.create_metrics(metric)
|
||||
# sleep 0.05 second between metrics to make sure timestamps
|
||||
# are different
|
||||
time.sleep(0.05)
|
||||
resp, response_body = \
|
||||
cls.monasca_client.list_alarms_state_history()
|
||||
elements = response_body['elements']
|
||||
if len(elements) >= 2:
|
||||
return
|
||||
else:
|
||||
num_transitions = len(elements)
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
assert False, "Required {} alarm state transitions, but found {}".\
|
||||
format(MIN_HISTORY, num_transitions)
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestAlarmStateHistoryMultipleTransitions, cls).\
|
||||
resource_cleanup()
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarm_state_history(self):
|
||||
# Get the alarm state history for a specific alarm by ID
|
||||
resp, response_body = self.monasca_client.list_alarms_state_history()
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
if elements:
|
||||
element = elements[0]
|
||||
alarm_id = element['alarm_id']
|
||||
resp, response_body = self.monasca_client.list_alarm_state_history(
|
||||
alarm_id)
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
# Test Response Body
|
||||
self.assertTrue(set(['links', 'elements']) ==
|
||||
set(response_body))
|
||||
elements = response_body['elements']
|
||||
links = response_body['links']
|
||||
self.assertIsInstance(links, list)
|
||||
link = links[0]
|
||||
self.assertTrue(set(['rel', 'href']) ==
|
||||
set(link))
|
||||
self.assertEqual(link['rel'], u'self')
|
||||
definition = elements[0]
|
||||
self.assertTrue(set(['id', 'alarm_id', 'metrics', 'new_state',
|
||||
'old_state', 'reason', 'reason_data',
|
||||
'sub_alarms', 'timestamp']) ==
|
||||
set(definition))
|
||||
else:
|
||||
error_msg = "Failed test_list_alarm_state_history: at least one " \
|
||||
"alarm state history is needed."
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarm_state_history_with_offset_limit(self):
|
||||
# Get the alarm state history for a specific alarm by ID
|
||||
resp, response_body = self.monasca_client.list_alarms_state_history()
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
if len(elements) >= MIN_HISTORY:
|
||||
element = elements[0]
|
||||
second_element = elements[1]
|
||||
alarm_id = element['alarm_id']
|
||||
query_parms = '?limit=1'
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_alarm_state_history(alarm_id, query_parms)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(1, len(elements))
|
||||
|
||||
query_parms = '?offset=' + str(element['timestamp'])
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_alarm_state_history(alarm_id, query_parms)
|
||||
elements_new = response_body['elements']
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(1, len(elements_new))
|
||||
self.assertEqual(second_element, elements_new[0])
|
||||
else:
|
||||
error_msg = "Failed test_list_alarm_state_history_with_offset" \
|
||||
"_limit: two alarms state history are needed."
|
||||
self.fail(error_msg)
|
||||
@@ -1,212 +0,0 @@
|
||||
# (C) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
|
||||
WAIT_SECS = 10
|
||||
|
||||
|
||||
class TestAlarmTransitions(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestAlarmTransitions, cls).resource_setup()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestAlarmTransitions, cls).resource_cleanup()
|
||||
|
||||
def _wait_for_alarm_creation(self, definition_id):
|
||||
for x in range(WAIT_SECS):
|
||||
time.sleep(1)
|
||||
resp, resp_body = self.monasca_client.list_alarms(
|
||||
query_params="?alarm_definition_id=" + definition_id)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
if len(resp_body['elements']) != 0:
|
||||
break
|
||||
self.assertEqual(1, len(resp_body['elements']))
|
||||
alarm_id = resp_body['elements'][0]['id']
|
||||
initial_state = resp_body['elements'][0]['state']
|
||||
return alarm_id, initial_state
|
||||
|
||||
def _wait_for_alarm_transition(self, alarm_id, expected_state):
|
||||
for x in range(WAIT_SECS):
|
||||
time.sleep(1)
|
||||
resp, resp_body = self.monasca_client.get_alarm(alarm_id)
|
||||
self.assertEqual(200, resp.status)
|
||||
if resp_body['state'] == expected_state:
|
||||
break
|
||||
self.assertEqual(expected_state, resp_body['state'])
|
||||
|
||||
def _send_measurement(self, metric_def, value):
|
||||
metric = helpers.create_metric(name=metric_def['name'],
|
||||
dimensions=metric_def['dimensions'],
|
||||
value=value)
|
||||
resp, resp_body = self.monasca_client.create_metrics([metric])
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_alarm_max_function(self):
|
||||
metric_def = {
|
||||
'name': data_utils.rand_name("max_test"),
|
||||
'dimensions': {
|
||||
'dim_to_match': data_utils.rand_name("max_match")
|
||||
}
|
||||
}
|
||||
expression = "max(" + metric_def['name'] + ") > 14"
|
||||
definition = helpers.create_alarm_definition(name="Test Max Function",
|
||||
description="",
|
||||
expression=expression,
|
||||
match_by=["dim_to_match"])
|
||||
resp, resp_body = (self.monasca_client
|
||||
.create_alarm_definitions(definition))
|
||||
self.assertEqual(201, resp.status)
|
||||
definition_id = resp_body['id']
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
self._send_measurement(metric_def, 1)
|
||||
|
||||
alarm_id, initial_state = self._wait_for_alarm_creation(definition_id)
|
||||
self.assertEqual("UNDETERMINED", initial_state)
|
||||
|
||||
self._send_measurement(metric_def, 20)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "ALARM")
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_alarm_max_with_deterministic(self):
|
||||
metric_def = {
|
||||
'name': data_utils.rand_name("max_deterministic_test"),
|
||||
'dimensions': {
|
||||
'dim_to_match': data_utils.rand_name("max_match")
|
||||
}
|
||||
}
|
||||
expression = "max(" + metric_def['name'] + ",deterministic) > 14"
|
||||
definition = helpers.create_alarm_definition(name="Test Max Deterministic Function",
|
||||
description="",
|
||||
expression=expression,
|
||||
match_by=["dim_to_match"])
|
||||
resp, resp_body = self.monasca_client.create_alarm_definitions(definition)
|
||||
self.assertEqual(201, resp.status)
|
||||
definition_id = resp_body['id']
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
self._send_measurement(metric_def, 1)
|
||||
|
||||
alarm_id, initial_state = self._wait_for_alarm_creation(definition_id)
|
||||
self.assertEqual("OK", initial_state)
|
||||
|
||||
self._send_measurement(metric_def, 20)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "ALARM")
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_alarm_last_function(self):
|
||||
metric_def = {
|
||||
'name': data_utils.rand_name("last_test"),
|
||||
'dimensions': {
|
||||
'dim_to_match': data_utils.rand_name("last_match")
|
||||
}
|
||||
}
|
||||
expression = "last(" + metric_def['name'] + ") > 14"
|
||||
definition = helpers.create_alarm_definition(name="Test Last Function",
|
||||
description="",
|
||||
expression=expression,
|
||||
match_by=["dim_to_match"])
|
||||
resp, resp_body = self.monasca_client.create_alarm_definitions(definition)
|
||||
self.assertEqual(201, resp.status)
|
||||
definition_id = resp_body['id']
|
||||
# Ensure the new Alarm Definition gets to the Threshold Engine
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
self._send_measurement(metric_def, 1)
|
||||
|
||||
alarm_id, initial_state = self._wait_for_alarm_creation(definition_id)
|
||||
self.assertEqual("OK", initial_state)
|
||||
|
||||
self._send_measurement(metric_def, 20)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "ALARM")
|
||||
|
||||
self._send_measurement(metric_def, 3)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "OK")
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_alarm_last_with_deterministic(self):
|
||||
metric_def = {
|
||||
'name': data_utils.rand_name("last_deterministic_test"),
|
||||
'dimensions': {
|
||||
'dim_to_match': data_utils.rand_name("last_match")
|
||||
}
|
||||
}
|
||||
expression = "last(" + metric_def['name'] + ",deterministic) > 14"
|
||||
definition = helpers.create_alarm_definition(name="Test Last Deterministic Function",
|
||||
description="",
|
||||
expression=expression,
|
||||
match_by=["dim_to_match"])
|
||||
resp, resp_body = self.monasca_client.create_alarm_definitions(definition)
|
||||
self.assertEqual(201, resp.status)
|
||||
definition_id = resp_body['id']
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
self._send_measurement(metric_def, 1)
|
||||
|
||||
alarm_id, initial_state = self._wait_for_alarm_creation(definition_id)
|
||||
self.assertEqual("OK", initial_state)
|
||||
|
||||
self._send_measurement(metric_def, 20)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "ALARM")
|
||||
|
||||
self._send_measurement(metric_def, 3)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "OK")
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_alarm_metric_mixcase(self):
|
||||
metric_def = {
|
||||
'name': data_utils.rand_name("MixCase_Test"),
|
||||
'dimensions': {
|
||||
'dim_to_match': data_utils.rand_name("max_match")
|
||||
}
|
||||
}
|
||||
expression = "max(" + metric_def['name'] + ") > 14"
|
||||
definition = helpers.create_alarm_definition(name="Metric Mixcase Test",
|
||||
description="",
|
||||
expression=expression,
|
||||
match_by=["dim_to_match"])
|
||||
resp, resp_body = self.monasca_client.create_alarm_definitions(definition)
|
||||
self.assertEqual(201, resp.status)
|
||||
definition_id = resp_body['id']
|
||||
|
||||
# Ensure the new Alarm Definition gets to the Threshold Engine
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
self._send_measurement(metric_def, 1)
|
||||
|
||||
alarm_id, initial_state = self._wait_for_alarm_creation(definition_id)
|
||||
self.assertEqual("UNDETERMINED", initial_state)
|
||||
|
||||
self._send_measurement(metric_def, 20)
|
||||
|
||||
self._wait_for_alarm_transition(alarm_id, "ALARM")
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,373 +0,0 @@
|
||||
# (C) Copyright 2016-2018 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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
|
||||
import urllib.parse as parse
|
||||
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
|
||||
|
||||
GROUP_BY_ALLOWED_PARAMS = {'alarm_definition_id', 'name', 'state', 'severity',
|
||||
'link', 'lifecycle_state', 'metric_name',
|
||||
'dimension_name', 'dimension_value'}
|
||||
|
||||
|
||||
class TestAlarmsCount(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestAlarmsCount, cls).resource_setup()
|
||||
|
||||
num_hosts = 20
|
||||
|
||||
alarm_definitions = []
|
||||
expected_alarm_counts = []
|
||||
metrics_to_send = []
|
||||
|
||||
# OK, LOW
|
||||
expression = "max(test_metric_01) > 10"
|
||||
name = data_utils.rand_name('test-counts-01')
|
||||
alarm_definitions.append(helpers.create_alarm_definition(
|
||||
name=name,
|
||||
expression=expression,
|
||||
severity='LOW',
|
||||
match_by=['hostname', 'unique']))
|
||||
for i in range(100):
|
||||
metrics_to_send.append(helpers.create_metric(
|
||||
name='test_metric_01',
|
||||
dimensions={'hostname': 'test_' + str(i % num_hosts),
|
||||
'unique': str(i)},
|
||||
value=1
|
||||
))
|
||||
expected_alarm_counts.append(100)
|
||||
|
||||
# ALARM, MEDIUM
|
||||
expression = "max(test_metric_02) > 10"
|
||||
name = data_utils.rand_name('test-counts-02')
|
||||
alarm_definitions.append(helpers.create_alarm_definition(
|
||||
name=name,
|
||||
expression=expression,
|
||||
severity='MEDIUM',
|
||||
match_by=['hostname', 'unique']))
|
||||
for i in range(75):
|
||||
metrics_to_send.append(helpers.create_metric(
|
||||
name='test_metric_02',
|
||||
dimensions={'hostname': 'test_' + str(i % num_hosts),
|
||||
'unique': str(i)},
|
||||
value=11
|
||||
))
|
||||
# append again to move from undetermined to alarm
|
||||
metrics_to_send.append(helpers.create_metric(
|
||||
name='test_metric_02',
|
||||
dimensions={'hostname': 'test_' + str(i % num_hosts),
|
||||
'unique': str(i)},
|
||||
value=11
|
||||
))
|
||||
expected_alarm_counts.append(75)
|
||||
|
||||
# OK, HIGH, shared dimension
|
||||
expression = "max(test_metric_03) > 100"
|
||||
name = data_utils.rand_name('test_counts-03')
|
||||
alarm_definitions.append(helpers.create_alarm_definition(
|
||||
name=name,
|
||||
expression=expression,
|
||||
severity='HIGH',
|
||||
match_by=['hostname', 'unique']))
|
||||
for i in range(50):
|
||||
metrics_to_send.append(helpers.create_metric(
|
||||
name='test_metric_03',
|
||||
dimensions={'hostname': 'test_' + str(i % num_hosts),
|
||||
'unique': str(i),
|
||||
'height': '55'},
|
||||
value=i
|
||||
))
|
||||
expected_alarm_counts.append(50)
|
||||
|
||||
# UNDERTERMINED, CRITICAL
|
||||
expression = "max(test_metric_undet) > 100"
|
||||
name = data_utils.rand_name('test-counts-04')
|
||||
alarm_definitions.append(helpers.create_alarm_definition(
|
||||
name=name,
|
||||
expression=expression,
|
||||
severity='CRITICAL',
|
||||
match_by=['hostname', 'unique']))
|
||||
for i in range(25):
|
||||
metrics_to_send.append(helpers.create_metric(
|
||||
name='test_metric_undet',
|
||||
dimensions={'hostname': 'test_' + str(i % num_hosts),
|
||||
'unique': str(i)},
|
||||
value=1
|
||||
))
|
||||
expected_alarm_counts.append(25)
|
||||
|
||||
# create alarm definitions
|
||||
cls.alarm_definition_ids = []
|
||||
for definition in alarm_definitions:
|
||||
resp, response_body = cls.monasca_client.create_alarm_definitions(
|
||||
definition)
|
||||
if resp.status == 201:
|
||||
cls.alarm_definition_ids.append(response_body['id'])
|
||||
else:
|
||||
msg = "Failed to create alarm_definition during setup: {} {}".format(resp.status,
|
||||
response_body)
|
||||
assert False, msg
|
||||
|
||||
# Give Thresh time to process the new Alarm Definitions
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
# create alarms
|
||||
for metric in metrics_to_send:
|
||||
metric['timestamp'] = int(time.time() * 1000)
|
||||
cls.monasca_client.create_metrics(metric)
|
||||
# ensure metric timestamps are unique
|
||||
time.sleep(0.01)
|
||||
|
||||
# check that alarms exist
|
||||
time_out = time.time() + 70
|
||||
while time.time() < time_out:
|
||||
setup_complete = True
|
||||
alarm_count = 0
|
||||
for i in range(len(cls.alarm_definition_ids)):
|
||||
resp, response_body = cls.monasca_client.list_alarms(
|
||||
'?alarm_definition_id=' + cls.alarm_definition_ids[i])
|
||||
if resp.status != 200:
|
||||
msg = "Error listing alarms: {} {}".format(resp.status, response_body)
|
||||
assert False, msg
|
||||
if len(response_body['elements']) < expected_alarm_counts[i]:
|
||||
setup_complete = False
|
||||
alarm_count += len(response_body['elements'])
|
||||
break
|
||||
|
||||
if setup_complete:
|
||||
# allow alarm transitions to occur
|
||||
# time.sleep(15)
|
||||
return
|
||||
|
||||
msg = "Failed to create all specified alarms" \
|
||||
" during setup, alarm_count was {}".format(alarm_count)
|
||||
assert False, msg
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestAlarmsCount, cls).resource_cleanup()
|
||||
|
||||
def _verify_counts_format(self, response_body, group_by=None, expected_length=None):
|
||||
expected_keys = ['links', 'counts', 'columns']
|
||||
for key in expected_keys:
|
||||
self.assertIn(key, response_body)
|
||||
self.assertIsInstance(response_body[key], list)
|
||||
|
||||
expected_columns = ['count']
|
||||
if isinstance(group_by, list):
|
||||
expected_columns.extend(group_by)
|
||||
self.assertEqual(expected_columns, response_body['columns'])
|
||||
|
||||
if expected_length is not None:
|
||||
self.assertEqual(expected_length, len(response_body['counts']))
|
||||
else:
|
||||
expected_length = len(response_body['counts'])
|
||||
|
||||
for i in range(expected_length):
|
||||
self.assertEqual(len(expected_columns), len(response_body['counts'][i]))
|
||||
|
||||
# test with no params
|
||||
@decorators.attr(type='gate')
|
||||
def test_count(self):
|
||||
resp, response_body = self.monasca_client.count_alarms()
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body)
|
||||
self.assertEqual(250, response_body['counts'][0][0])
|
||||
|
||||
# test with each group_by parameter singularly
|
||||
@decorators.attr(type='gate')
|
||||
def test_group_by_singular(self):
|
||||
resp, response_body = self.monasca_client.list_alarms("?state=ALARM")
|
||||
self.assertEqual(200, resp.status)
|
||||
alarm_state_count = len(response_body['elements'])
|
||||
resp, response_body = self.monasca_client.list_alarms("?state=undetermined")
|
||||
self.assertEqual(200, resp.status)
|
||||
undet_state_count = len(response_body['elements'])
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms("?group_by=state")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body, group_by=['state'])
|
||||
|
||||
self.assertEqual('ALARM', response_body['counts'][0][1])
|
||||
self.assertEqual(alarm_state_count, response_body['counts'][0][0])
|
||||
self.assertEqual('UNDETERMINED', response_body['counts'][-1][1])
|
||||
self.assertEqual(undet_state_count, response_body['counts'][-1][0])
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms("?group_by=name")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body, group_by=['name'], expected_length=4)
|
||||
|
||||
# test with group by a parameter that is not allowed
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_group_by_not_allowed(self):
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.count_alarms, "?group_by=not_allowed")
|
||||
|
||||
# test with a few group_by fields
|
||||
@decorators.attr(type='gate')
|
||||
def test_group_by_multiple(self):
|
||||
resp, response_body = self.monasca_client.list_alarms()
|
||||
alarm_low_count = 0
|
||||
for alarm in response_body['elements']:
|
||||
if alarm['state'] == 'ALARM' and alarm.get('severity', '') == 'LOW':
|
||||
alarm_low_count += 1
|
||||
|
||||
# Using urlencode mimics the CLI behavior. Without the urlencode, falcon
|
||||
# treats group_by as a list, with the urlencode it treats group_by as
|
||||
# a string. The API needs to handle both.
|
||||
# test_with_all_group_by_params tests multiple group_by without
|
||||
# urlencode
|
||||
query_params = parse.urlencode([('group_by', 'state,severity')])
|
||||
resp, response_body = self.monasca_client.count_alarms("?" + query_params)
|
||||
self._verify_counts_format(response_body, group_by=['state', 'severity'])
|
||||
|
||||
def run_count_test(self, query_string):
|
||||
resp, response_body = self.monasca_client.list_alarms(query_string)
|
||||
self.assertEqual(200, resp.status)
|
||||
expected_count = len(response_body['elements'])
|
||||
# Make sure something was found
|
||||
self.assertTrue(expected_count > 0)
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms(query_string)
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body, expected_length=1)
|
||||
self.assertEqual(expected_count, response_body['counts'][0][0])
|
||||
|
||||
# test filter by severity
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_severity(self):
|
||||
self.run_count_test("?severity=LOW")
|
||||
|
||||
# test filter by state
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_state(self):
|
||||
self.run_count_test("?state=ALARM")
|
||||
|
||||
# test filter by metric name
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_metric_name(self):
|
||||
self.run_count_test("?metric_name=test_metric_01")
|
||||
|
||||
# test with multiple metric dimensions
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_multiple_dimensions(self):
|
||||
self.run_count_test("?metric_dimensions=hostname:test_1,unique:1")
|
||||
|
||||
# test with multiple metric dimensions values
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_multiple_dimensions_values_1(self):
|
||||
self.run_count_test("?metric_dimensions=hostname:test_1|test_2,unique:1")
|
||||
|
||||
# test with multiple metric dimensions values (order should not matter)
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_multiple_dimensions_values_2(self):
|
||||
self.run_count_test("?metric_dimensions=hostname:test_2|test_1,unique:1")
|
||||
|
||||
# test with filter and group_by parameters
|
||||
@decorators.attr(type='gate')
|
||||
def test_filter_and_group_by_params(self):
|
||||
resp, response_body = self.monasca_client.list_alarms("?state=ALARM")
|
||||
self.assertEqual(200, resp.status)
|
||||
expected_count = 0
|
||||
for element in response_body['elements']:
|
||||
if element['alarm_definition']['severity'] == 'MEDIUM':
|
||||
expected_count += 1
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms("?state=ALARM&group_by=severity")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body, group_by=['severity'])
|
||||
self.assertEqual(expected_count, response_body['counts'][0][0])
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_with_all_group_by_params(self):
|
||||
resp, response_body = self.monasca_client.list_alarms()
|
||||
self.assertEqual(200, resp.status)
|
||||
expected_num_count = len(response_body['elements'])
|
||||
|
||||
query_params = "?group_by=" + ','.join(GROUP_BY_ALLOWED_PARAMS)
|
||||
resp, response_body = self.monasca_client.count_alarms(query_params)
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body, group_by=list(GROUP_BY_ALLOWED_PARAMS))
|
||||
|
||||
# Expect duplicates
|
||||
msg = "Not enough distinct counts. Expected at " \
|
||||
"least {}, found {}".format(expected_num_count, len(response_body['counts']))
|
||||
assert expected_num_count <= len(response_body['counts']), msg
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_limit(self):
|
||||
resp, response_body = self.monasca_client.count_alarms(
|
||||
"?group_by=metric_name,dimension_name,dimension_value")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body,
|
||||
group_by=['metric_name', 'dimension_name', 'dimension_value'])
|
||||
assert len(response_body['counts']) > 1, "Too few counts to test limit, found 1"
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms(
|
||||
"?group_by=metric_name,dimension_name,dimension_value&limit=1")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body,
|
||||
group_by=['metric_name', 'dimension_name', 'dimension_value'],
|
||||
expected_length=1)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_offset(self):
|
||||
resp, response_body = self.monasca_client.count_alarms(
|
||||
"?group_by=metric_name,dimension_name,dimension_value")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body,
|
||||
group_by=['metric_name', 'dimension_name', 'dimension_value'])
|
||||
expected_counts = len(response_body['counts']) - 1
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms(
|
||||
"?group_by=metric_name,dimension_name,dimension_value&offset=1")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body,
|
||||
group_by=['metric_name', 'dimension_name', 'dimension_value'],
|
||||
expected_length=expected_counts)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_invalid_offset(self):
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.count_alarms,
|
||||
"?group_by=metric_name&offset=not_an_int")
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_limit_and_offset(self):
|
||||
resp, response_body = self.monasca_client.count_alarms(
|
||||
"?group_by=metric_name,dimension_name,dimension_value")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body,
|
||||
group_by=['metric_name', 'dimension_name', 'dimension_value'])
|
||||
expected_first_result = response_body['counts'][1]
|
||||
|
||||
resp, response_body = self.monasca_client.count_alarms(
|
||||
"?group_by=metric_name,dimension_name,dimension_value&offset=1&limit=5")
|
||||
self.assertEqual(200, resp.status)
|
||||
self._verify_counts_format(response_body,
|
||||
group_by=['metric_name', 'dimension_name', 'dimension_value'],
|
||||
expected_length=5)
|
||||
self.assertEqual(expected_first_result, response_body['counts'][0])
|
||||
@@ -1,239 +0,0 @@
|
||||
# (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
from oslo_utils import timeutils
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
NUM_ALARM_DEFINITIONS = 3
|
||||
MIN_HISTORY = 3
|
||||
|
||||
|
||||
class TestAlarmsStateHistoryOneTransition(base.BaseMonascaTest):
|
||||
# Alarms state histories with one transition but different alarm
|
||||
# definitions are needed for this test class.
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestAlarmsStateHistoryOneTransition, cls).resource_setup()
|
||||
|
||||
for i in range(MIN_HISTORY):
|
||||
alarm_definition = helpers.create_alarm_definition(
|
||||
name=data_utils.rand_name('alarm_state_history' + str(i + 1)),
|
||||
expression="min(name-" + str(i + 1) + ") < " + str(i + 1))
|
||||
cls.monasca_client.create_alarm_definitions(alarm_definition)
|
||||
# Ensure the new Alarm Definitions get to the Threshold Engine
|
||||
time.sleep(constants.ALARM_DEFINITION_CREATION_WAIT)
|
||||
|
||||
num_transitions = 0
|
||||
for timer in range(constants.MAX_RETRIES):
|
||||
for i in range(MIN_HISTORY):
|
||||
# Create some metrics to prime the system and waiting for the
|
||||
# alarms to be created and then for them to change state.
|
||||
# MIN_HISTORY number of Alarms State History are needed.
|
||||
metric = helpers.create_metric(name="name-" + str(i + 1))
|
||||
cls.monasca_client.create_metrics(metric)
|
||||
# Ensure alarms transition at different times
|
||||
time.sleep(0.1)
|
||||
resp, response_body = cls.monasca_client.\
|
||||
list_alarms_state_history()
|
||||
elements = response_body['elements']
|
||||
if len(elements) >= MIN_HISTORY:
|
||||
return
|
||||
else:
|
||||
num_transitions = len(elements)
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
assert False, "Required {} alarm state transitions, but found {}".\
|
||||
format(MIN_HISTORY, num_transitions)
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestAlarmsStateHistoryOneTransition, cls).resource_cleanup()
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_state_history(self):
|
||||
resp, response_body = self.monasca_client.list_alarms_state_history()
|
||||
self.assertEqual(200, resp.status)
|
||||
# Test response body
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
elements = response_body['elements']
|
||||
number_of_alarms = len(elements)
|
||||
if number_of_alarms < 1:
|
||||
error_msg = "Failed test_list_alarms_state_history: need " \
|
||||
"at least one alarms state history to test."
|
||||
self.fail(error_msg)
|
||||
else:
|
||||
element = elements[0]
|
||||
self.assertTrue(set(['id', 'alarm_id', 'metrics', 'old_state',
|
||||
'new_state', 'reason', 'reason_data',
|
||||
'timestamp', 'sub_alarms'])
|
||||
== set(element))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_state_history_with_dimensions(self):
|
||||
resp, response_body = self.monasca_client.list_alarms_state_history()
|
||||
elements = response_body['elements']
|
||||
if elements:
|
||||
element = elements[0]
|
||||
dimension = element['metrics'][0]['dimensions']
|
||||
dimension_items = list(dimension.items())
|
||||
dimension_item = dimension_items[0]
|
||||
dimension_item_0 = dimension_item[0]
|
||||
dimension_item_1 = dimension_item[1]
|
||||
name = element['metrics'][0]['name']
|
||||
|
||||
query_parms = '?dimensions=' + str(dimension_item_0) + ':' + str(
|
||||
dimension_item_1)
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_alarms_state_history(query_parms)
|
||||
name_new = response_body['elements'][0]['metrics'][0]['name']
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(name, name_new)
|
||||
else:
|
||||
error_msg = "Failed test_list_alarms_state_history_with_" \
|
||||
"dimensions: need at least one alarms state history " \
|
||||
"to test."
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_state_history_with_start_time(self):
|
||||
# 1, get all histories
|
||||
resp, all_response_body = self.monasca_client.\
|
||||
list_alarms_state_history()
|
||||
all_elements = all_response_body['elements']
|
||||
|
||||
if len(all_elements) < 3:
|
||||
error_msg = "Failed test_list_alarms_state_history_with_" \
|
||||
"start_time: need 3 or more alarms state history " \
|
||||
"to test."
|
||||
self.fail(error_msg)
|
||||
|
||||
# 2, query second(timestamp) <= x
|
||||
min_element, second_element, max_element = \
|
||||
self._get_elements_with_min_max_timestamp(all_elements)
|
||||
start_time = second_element['timestamp']
|
||||
query_params = '?start_time=' + str(start_time)
|
||||
resp, selected_response_body = self.monasca_client.\
|
||||
list_alarms_state_history(query_params)
|
||||
selected_elements = selected_response_body['elements']
|
||||
|
||||
# 3. compare #1 and #2
|
||||
expected_elements = all_elements
|
||||
expected_elements.remove(min_element)
|
||||
self.assertEqual(expected_elements, selected_elements)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_state_history_with_end_time(self):
|
||||
# 1, get all histories
|
||||
resp, all_response_body = self.monasca_client.\
|
||||
list_alarms_state_history()
|
||||
all_elements = all_response_body['elements']
|
||||
|
||||
if len(all_elements) < 3:
|
||||
error_msg = "Failed test_list_alarms_state_history_with_" \
|
||||
"end_time: need 3 or more alarms state history " \
|
||||
"to test."
|
||||
self.fail(error_msg)
|
||||
|
||||
# 2, query x <= second(timestamp)
|
||||
min_element, second_element, max_element = \
|
||||
self._get_elements_with_min_max_timestamp(all_elements)
|
||||
end_time = second_element['timestamp']
|
||||
query_params = '?end_time=' + str(end_time)
|
||||
resp, selected_response_body = self.monasca_client.\
|
||||
list_alarms_state_history(query_params)
|
||||
selected_elements = selected_response_body['elements']
|
||||
|
||||
# 3. compare #1 and #2
|
||||
expected_elements = all_elements
|
||||
expected_elements.remove(max_element)
|
||||
self.assertEqual(expected_elements, selected_elements)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_state_history_with_start_end_time(self):
|
||||
# 1, get all histories
|
||||
resp, all_response_body = self.monasca_client.\
|
||||
list_alarms_state_history()
|
||||
all_elements = all_response_body['elements']
|
||||
|
||||
if len(all_elements) < 3:
|
||||
error_msg = "Failed test_list_alarms_state_history_with_" \
|
||||
"start_end_time: need 3 or more alarms state history" \
|
||||
"to test."
|
||||
self.fail(error_msg)
|
||||
|
||||
# 2, query min(timestamp) <= x <= max(timestamp)
|
||||
min_element, second_element, max_element = \
|
||||
self._get_elements_with_min_max_timestamp(all_elements)
|
||||
start_time = min_element['timestamp']
|
||||
end_time = max_element['timestamp']
|
||||
query_params = '?start_time=' + str(start_time) + '&end_time=' + \
|
||||
str(end_time)
|
||||
resp, selected_response_body = self.monasca_client.\
|
||||
list_alarms_state_history(query_params)
|
||||
selected_elements = selected_response_body['elements']
|
||||
|
||||
# 3. compare #1 and #2
|
||||
self.assertEqual(all_elements, selected_elements)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_state_history_with_offset_limit(self):
|
||||
resp, response_body = self.monasca_client.list_alarms_state_history()
|
||||
elements_set1 = response_body['elements']
|
||||
number_of_alarms = len(elements_set1)
|
||||
if number_of_alarms >= MIN_HISTORY:
|
||||
query_parms = '?limit=' + str(number_of_alarms)
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_alarms_state_history(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements_set2 = response_body['elements']
|
||||
self.assertEqual(number_of_alarms, len(elements_set2))
|
||||
for index in range(MIN_HISTORY - 1):
|
||||
self.assertEqual(elements_set1[index], elements_set2[index])
|
||||
for index in range(MIN_HISTORY - 1):
|
||||
alarm_history = elements_set2[index]
|
||||
max_limit = len(elements_set2) - index
|
||||
for limit in range(1, max_limit):
|
||||
first_index = index + 1
|
||||
last_index = first_index + limit
|
||||
expected_elements = elements_set2[first_index:last_index]
|
||||
|
||||
query_parms = '?offset=' + str(alarm_history['timestamp'])\
|
||||
+ '&limit=' + str(limit)
|
||||
resp, response_body = self.\
|
||||
monasca_client.list_alarms_state_history(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
new_elements = response_body['elements']
|
||||
self.assertEqual(limit, len(new_elements))
|
||||
for i in range(len(expected_elements)):
|
||||
self.assertEqual(expected_elements[i], new_elements[i])
|
||||
else:
|
||||
error_msg = ("Failed test_list_alarms_state_history_with_offset "
|
||||
"limit: need three alarms state history to test. "
|
||||
"Current number of alarms = {}").format(
|
||||
number_of_alarms)
|
||||
self.fail(error_msg)
|
||||
|
||||
def _get_elements_with_min_max_timestamp(self, elements):
|
||||
sorted_elements = sorted(elements, key=lambda element: timeutils.
|
||||
parse_isotime(element['timestamp']))
|
||||
min_element = sorted_elements[0]
|
||||
second_element = sorted_elements[1]
|
||||
max_element = sorted_elements[-1]
|
||||
return min_element, second_element, max_element
|
||||
@@ -1,334 +0,0 @@
|
||||
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||
# (C) Copyright 2017 SUSE LLC
|
||||
#
|
||||
# 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 urllib.parse import urlencode
|
||||
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
|
||||
|
||||
class TestDimensions(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestDimensions, cls).resource_setup()
|
||||
start_timestamp = int(round(time.time() * 1000))
|
||||
start_time_iso = helpers.timestamp_to_iso(start_timestamp)
|
||||
# NOTE (brtknr): use interval of a day because the tag based queries
|
||||
# appear to only support smallest granularity of a day, and disregard
|
||||
# time of day, which is fine for most use cases.
|
||||
day = 60 * 60 * 24 * 1000
|
||||
end_timestamp = start_timestamp + 10 * day
|
||||
end_time_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
|
||||
metric_name1 = data_utils.rand_name()
|
||||
name1 = "name_1"
|
||||
name2 = "name_2"
|
||||
value1 = "value_1"
|
||||
value2 = "value_2"
|
||||
timestamp1 = start_timestamp - day
|
||||
timestamp2 = start_timestamp + day
|
||||
timestamp3 = start_timestamp + day + day
|
||||
timestamp4 = end_timestamp + day
|
||||
metric1 = helpers.create_metric(name=metric_name1,
|
||||
timestamp=timestamp1,
|
||||
dimensions={name1: value1,
|
||||
name2: value2,
|
||||
})
|
||||
cls.monasca_client.create_metrics(metric1)
|
||||
metric1 = helpers.create_metric(name=metric_name1,
|
||||
timestamp=timestamp2,
|
||||
dimensions={name1: value2})
|
||||
cls.monasca_client.create_metrics(metric1)
|
||||
|
||||
metric_name2 = data_utils.rand_name()
|
||||
name3 = "name_3"
|
||||
value3 = "value_3"
|
||||
value4 = "value_4"
|
||||
metric2 = helpers.create_metric(name=metric_name2,
|
||||
timestamp=timestamp3,
|
||||
dimensions={name1: value3,
|
||||
name3: value4,
|
||||
})
|
||||
cls.monasca_client.create_metrics(metric2)
|
||||
|
||||
metric_name3 = data_utils.rand_name()
|
||||
metric3 = helpers.create_metric(name=metric_name3,
|
||||
timestamp=timestamp4,
|
||||
dimensions={name2: value3})
|
||||
cls.monasca_client.create_metrics(metric3)
|
||||
|
||||
cls._test_metric1 = metric1
|
||||
cls._test_metric2 = metric2
|
||||
cls._test_metric_names = {metric_name1, metric_name2, metric_name3}
|
||||
cls._dim_names_metric1 = [name1, name2]
|
||||
cls._dim_names_metric1_in_timerange = [name1]
|
||||
cls._dim_names_metric2 = [name1, name3]
|
||||
cls._dim_names_metric2_in_timerange = [name1, name3]
|
||||
cls._dim_names = sorted(set(cls._dim_names_metric1
|
||||
+ cls._dim_names_metric2))
|
||||
cls._dim_names_in_timerange = sorted(set(
|
||||
cls._dim_names_metric1_in_timerange +
|
||||
cls._dim_names_metric2_in_timerange))
|
||||
cls._dim_name1 = name1
|
||||
cls._dim_name1_values_for_metric1 = [value1, value2]
|
||||
cls._dim_name1_values_for_metric1_in_timerange = [value2]
|
||||
cls._dim_name1_values = [value1, value2, value3]
|
||||
cls._dim_name1_values_in_timerange = [value2, value3]
|
||||
cls._start_time = start_time_iso
|
||||
cls._end_time = end_time_iso
|
||||
|
||||
param = '?start_time=' + start_time_iso
|
||||
returned_name_set = set()
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = cls.monasca_client.list_metrics(
|
||||
param)
|
||||
elements = response_body['elements']
|
||||
metric_name1_count = 0
|
||||
for element in elements:
|
||||
returned_name_set.add(str(element['name']))
|
||||
if (str(element['name']) == metric_name1):
|
||||
metric_name1_count += 1
|
||||
# Java version of influxdb never returns both metric1 in the list but Python does.
|
||||
if cls._test_metric_names.issubset(returned_name_set) \
|
||||
and (metric_name1_count == 2 or i == constants.MAX_RETRIES - 1):
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
|
||||
assert False, 'Unable to initialize metrics'
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestDimensions, cls).resource_cleanup()
|
||||
|
||||
def _test_list_dimension_values_without_metric_name(self, timerange):
|
||||
param = '?dimension_name=' + self._dim_name1
|
||||
if timerange:
|
||||
param += '&start_time=' + self._start_time
|
||||
param += '&end_time=' + self._end_time
|
||||
resp, response_body = self.monasca_client.list_dimension_values(param)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue({'links', 'elements'} == set(response_body))
|
||||
response_values_length = len(response_body['elements'])
|
||||
values = [str(response_body['elements'][i]['dimension_value'])
|
||||
for i in range(response_values_length)]
|
||||
if timerange:
|
||||
self.assertEqual(values, self._dim_name1_values_in_timerange)
|
||||
else:
|
||||
self.assertEqual(values, self._dim_name1_values)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_dimension_values_without_metric_name(self):
|
||||
self._test_list_dimension_values_without_metric_name(timerange=False)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type='timerange')
|
||||
def test_list_dimension_values_without_metric_name_with_timerange(self):
|
||||
self._test_list_dimension_values_without_metric_name(timerange=True)
|
||||
|
||||
def _test_list_dimension_values_with_metric_name(self, timerange):
|
||||
param = '?metric_name=' + self._test_metric1['name']
|
||||
param += '&dimension_name=' + self._dim_name1
|
||||
if timerange:
|
||||
param += '&start_time=' + self._start_time
|
||||
param += '&end_time=' + self._end_time
|
||||
resp, response_body = self.monasca_client.list_dimension_values(param)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue({'links', 'elements'} == set(response_body))
|
||||
response_values_length = len(response_body['elements'])
|
||||
values = [str(response_body['elements'][i]['dimension_value'])
|
||||
for i in range(response_values_length)]
|
||||
if timerange:
|
||||
self.assertEqual(values, self._dim_name1_values_for_metric1_in_timerange)
|
||||
else:
|
||||
self.assertEqual(values, self._dim_name1_values_for_metric1)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_dimension_values_with_metric_name(self):
|
||||
self._test_list_dimension_values_with_metric_name(timerange=False)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type='timerange')
|
||||
def test_list_dimension_values_with_metric_name_with_timerange(self):
|
||||
self._test_list_dimension_values_with_metric_name(timerange=True)
|
||||
|
||||
def _test_list_dimension_values_limit_and_offset(self, timerange):
|
||||
param = '?dimension_name=' + self._dim_name1
|
||||
if timerange:
|
||||
param += '&start_time=' + self._start_time
|
||||
param += '&end_time=' + self._end_time
|
||||
resp, response_body = self.monasca_client.list_dimension_values(param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
num_dim_values = len(elements)
|
||||
for limit in range(1, num_dim_values):
|
||||
start_index = 0
|
||||
params = [('limit', limit)]
|
||||
offset = None
|
||||
while True:
|
||||
num_expected_elements = limit
|
||||
if (num_expected_elements + start_index) > num_dim_values:
|
||||
num_expected_elements = num_dim_values - start_index
|
||||
|
||||
these_params = list(params)
|
||||
# Use the offset returned by the last call if available
|
||||
if offset:
|
||||
these_params.extend([('offset', str(offset))])
|
||||
query_param = '?dimension_name=' + self._dim_name1
|
||||
if timerange:
|
||||
query_param += '&start_time=' + self._start_time
|
||||
query_param += '&end_time=' + self._end_time
|
||||
query_param += '&' + urlencode(these_params)
|
||||
resp, response_body = \
|
||||
self.monasca_client.list_dimension_values(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
if not response_body['elements']:
|
||||
self.fail("No metrics returned")
|
||||
response_values_length = len(response_body['elements'])
|
||||
if response_values_length == 0:
|
||||
self.fail("No dimension names returned")
|
||||
new_elements = [str(response_body['elements'][i]
|
||||
['dimension_value']) for i in
|
||||
range(response_values_length)]
|
||||
self.assertEqual(num_expected_elements, len(new_elements))
|
||||
|
||||
expected_elements = elements[start_index:start_index + limit]
|
||||
expected_dimension_values = \
|
||||
[expected_elements[i]['dimension_value'] for i in range(
|
||||
len(expected_elements))]
|
||||
self.assertEqual(expected_dimension_values, new_elements)
|
||||
start_index += num_expected_elements
|
||||
if start_index >= num_dim_values:
|
||||
break
|
||||
# Get the next set
|
||||
offset = self._get_offset(response_body)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_dimension_values_limit_and_offset(self):
|
||||
self._test_list_dimension_values_limit_and_offset(timerange=False)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type='timerange')
|
||||
def test_list_dimension_values_limit_and_offset_with_timerange(self):
|
||||
self._test_list_dimension_values_limit_and_offset(timerange=True)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_dimension_values_no_dimension_name(self):
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_dimension_values)
|
||||
|
||||
@decorators.attr(type=["gate", "smoke"])
|
||||
def test_list_dimension_names(self):
|
||||
resp, response_body = self.monasca_client.list_dimension_names()
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue({'links', 'elements'} == set(response_body))
|
||||
response_names_length = len(response_body['elements'])
|
||||
names = [str(response_body['elements'][i]['dimension_name']) for i
|
||||
in range(response_names_length)]
|
||||
self.assertEqual(names, self._dim_names)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_dimension_names_with_metric_name(self):
|
||||
self._test_list_dimension_names_with_metric_name(
|
||||
self._test_metric1['name'], self._dim_names_metric1,
|
||||
timerange=False)
|
||||
self._test_list_dimension_names_with_metric_name(
|
||||
self._test_metric2['name'], self._dim_names_metric2,
|
||||
timerange=False)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type='timerange')
|
||||
def test_list_dimension_names_with_metric_name_with_timerange(self):
|
||||
self._test_list_dimension_names_with_metric_name(
|
||||
self._test_metric1['name'],
|
||||
self._dim_names_metric1_in_timerange,
|
||||
timerange=True)
|
||||
self._test_list_dimension_names_with_metric_name(
|
||||
self._test_metric2['name'],
|
||||
self._dim_names_metric2_in_timerange,
|
||||
timerange=True)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_dimension_names_limit_and_offset(self):
|
||||
resp, response_body = self.monasca_client.list_dimension_names()
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
num_dim_names = len(elements)
|
||||
for limit in range(1, num_dim_names):
|
||||
start_index = 0
|
||||
params = [('limit', limit)]
|
||||
offset = None
|
||||
while True:
|
||||
num_expected_elements = limit
|
||||
if (num_expected_elements + start_index) > num_dim_names:
|
||||
num_expected_elements = num_dim_names - start_index
|
||||
|
||||
these_params = list(params)
|
||||
# If not the first call, use the offset returned by the last
|
||||
# call
|
||||
if offset:
|
||||
these_params.extend([('offset', str(offset))])
|
||||
query_param = '?' + urlencode(these_params)
|
||||
resp, response_body = self.monasca_client.list_dimension_names(
|
||||
query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
if not response_body['elements']:
|
||||
self.fail("No metrics returned")
|
||||
response_names_length = len(response_body['elements'])
|
||||
if response_names_length == 0:
|
||||
self.fail("No dimension names returned")
|
||||
new_elements = [str(response_body['elements'][i]
|
||||
['dimension_name']) for i in
|
||||
range(response_names_length)]
|
||||
self.assertEqual(num_expected_elements, len(new_elements))
|
||||
|
||||
expected_elements = elements[start_index:start_index + limit]
|
||||
expected_dimension_names = \
|
||||
[expected_elements[i]['dimension_name'] for i in range(
|
||||
len(expected_elements))]
|
||||
self.assertEqual(expected_dimension_names, new_elements)
|
||||
start_index += num_expected_elements
|
||||
if start_index >= num_dim_names:
|
||||
break
|
||||
# Get the next set
|
||||
offset = self._get_offset(response_body)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_dimension_names_with_wrong_metric_name(self):
|
||||
self._test_list_dimension_names_with_metric_name(
|
||||
'wrong_metric_name', [], timerange=False)
|
||||
|
||||
def _test_list_dimension_names_with_metric_name(self, metric_name,
|
||||
dimension_names,
|
||||
timerange):
|
||||
param = '?metric_name=' + metric_name
|
||||
if timerange:
|
||||
param += '&start_time=' + self._start_time
|
||||
param += '&end_time=' + self._end_time
|
||||
resp, response_body = self.monasca_client.list_dimension_names(param)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
response_names_length = len(response_body['elements'])
|
||||
names = [str(response_body['elements'][i]['dimension_name']) for i
|
||||
in range(response_names_length)]
|
||||
self.assertEqual(names, dimension_names)
|
||||
@@ -1,410 +0,0 @@
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
NUM_MEASUREMENTS = 50
|
||||
ONE_SECOND = 1000
|
||||
|
||||
|
||||
class TestMeasurements(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestMeasurements, cls).resource_setup()
|
||||
|
||||
start_timestamp = int(time.time() * 1000)
|
||||
start_time = str(helpers.timestamp_to_iso(start_timestamp))
|
||||
metrics = []
|
||||
name1 = data_utils.rand_name()
|
||||
name2 = data_utils.rand_name()
|
||||
cls._names_list = [name1, name2]
|
||||
key = data_utils.rand_name('key')
|
||||
value = data_utils.rand_name('value')
|
||||
cls._key = key
|
||||
cls._value = value
|
||||
cls._start_timestamp = start_timestamp
|
||||
|
||||
for i in range(NUM_MEASUREMENTS):
|
||||
metric = helpers.create_metric(
|
||||
name=name1,
|
||||
timestamp=start_timestamp + (i * 10),
|
||||
value=i)
|
||||
metrics.append(metric)
|
||||
cls.monasca_client.create_metrics(metrics)
|
||||
|
||||
# Create metric2 for test_list_measurements_with_dimensions
|
||||
metric2 = helpers.create_metric(
|
||||
name=name1, timestamp=start_timestamp + ONE_SECOND * 2,
|
||||
dimensions={key: value}, value=NUM_MEASUREMENTS)
|
||||
cls.monasca_client.create_metrics(metric2)
|
||||
|
||||
# Create metric3 for test_list_measurements_with_offset_limit
|
||||
metric3 = [
|
||||
helpers.create_metric(
|
||||
name=name2, timestamp=start_timestamp + ONE_SECOND * 3,
|
||||
dimensions={'key1': 'value1', 'key2': 'value5', 'key3': 'value7'}),
|
||||
helpers.create_metric(
|
||||
name=name2, timestamp=start_timestamp + ONE_SECOND * 3 + 10,
|
||||
dimensions={'key1': 'value2', 'key2': 'value5', 'key3': 'value7'}),
|
||||
helpers.create_metric(
|
||||
name=name2, timestamp=start_timestamp + ONE_SECOND * 3 + 20,
|
||||
dimensions={'key1': 'value3', 'key2': 'value6', 'key3': 'value7'}),
|
||||
helpers.create_metric(
|
||||
name=name2, timestamp=start_timestamp + ONE_SECOND * 3 + 30,
|
||||
dimensions={'key1': 'value4', 'key2': 'value6', 'key3': 'value8'})
|
||||
]
|
||||
cls.monasca_client.create_metrics(metric3)
|
||||
|
||||
# Create metric3 for test_list_measurements_with_no_merge_metrics
|
||||
metric4 = helpers.create_metric(
|
||||
name=name1, timestamp=start_timestamp + ONE_SECOND * 4,
|
||||
dimensions={'key-1': 'value-1'},
|
||||
value=NUM_MEASUREMENTS + 1)
|
||||
cls.monasca_client.create_metrics(metric4)
|
||||
|
||||
end_time = str(helpers.timestamp_to_iso(
|
||||
start_timestamp + NUM_MEASUREMENTS + ONE_SECOND * 5))
|
||||
queries = []
|
||||
queries.append('?name={}&start_time={}&end_time={}&merge_metrics=true'.
|
||||
format(name1, start_time, end_time))
|
||||
queries.append('?name={}&start_time={}&end_time={}&merge_metrics=true'.
|
||||
format(name2, start_time, end_time))
|
||||
|
||||
for timer in range(constants.MAX_RETRIES):
|
||||
responses = list(map(cls.monasca_client.list_measurements, queries))
|
||||
resp_first = responses[0][0]
|
||||
response_body_first = responses[0][1]
|
||||
resp_second = responses[1][0]
|
||||
response_body_second = responses[1][1]
|
||||
if resp_first.status == 200 and resp_second.status == 200 \
|
||||
and len(response_body_first['elements']) == 1 \
|
||||
and len(response_body_second['elements']) == 1:
|
||||
len_meas_first = len(
|
||||
response_body_first['elements'][0]['measurements'])
|
||||
len_meas_second = len(
|
||||
response_body_second['elements'][0]['measurements'])
|
||||
if len_meas_first == NUM_MEASUREMENTS + 2 \
|
||||
and len_meas_second == 4:
|
||||
break
|
||||
else:
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
else:
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
|
||||
cls._start_time = start_time
|
||||
cls._end_time = end_time
|
||||
|
||||
@decorators.attr(type=["gate", "smoke"])
|
||||
def test_list_measurements(self):
|
||||
query_parms = '?name=' + str(self._names_list[0]) + \
|
||||
'&merge_metrics=true' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self._verify_list_measurements(resp, response_body)
|
||||
elements = response_body['elements']
|
||||
self._verify_list_measurements_elements(
|
||||
elements=elements, test_key=None, test_value=None)
|
||||
measurements = elements[0]['measurements']
|
||||
self._verify_list_measurements_meas_len(
|
||||
measurements, test_len=NUM_MEASUREMENTS + 2)
|
||||
i = 0
|
||||
for measurement in measurements:
|
||||
self._verify_list_measurements_measurement(measurement, i)
|
||||
i += 1
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_measurements_with_no_start_time(self):
|
||||
query_parms = '?name=' + str(self._names_list[0])
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_measurements, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_measurements_with_no_name(self):
|
||||
query_parms = '?start_time=' + str(self._start_time) + '&end_time=' + \
|
||||
str(self._end_time)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_measurements, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_dimensions(self):
|
||||
query_parms = '?name=' + self._names_list[0] + '&start_time=' + \
|
||||
str(self._start_time) + '&end_time=' + \
|
||||
str(self._end_time) + '&dimensions=' + self._key + ':' \
|
||||
+ self._value
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self._verify_list_measurements(resp, response_body)
|
||||
elements = response_body['elements']
|
||||
self._verify_list_measurements_elements(
|
||||
elements=elements, test_key=None, test_value=None)
|
||||
measurements = elements[0]['measurements']
|
||||
self._verify_list_measurements_meas_len(measurements, 1)
|
||||
measurement = measurements[0]
|
||||
self._verify_list_measurements_measurement(
|
||||
measurement=measurement, test_value=NUM_MEASUREMENTS)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_endtime(self):
|
||||
time_iso = helpers.timestamp_to_iso(
|
||||
self._start_timestamp + ONE_SECOND * 2)
|
||||
query_parms = '?name=' + str(self._names_list[0]) + \
|
||||
'&merge_metrics=true' \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(time_iso)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self._verify_list_measurements(resp, response_body)
|
||||
elements = response_body['elements']
|
||||
self._verify_list_measurements_elements(elements=elements,
|
||||
test_key=None, test_value=None)
|
||||
measurements = elements[0]['measurements']
|
||||
self._verify_list_measurements_meas_len(measurements=measurements,
|
||||
test_len=NUM_MEASUREMENTS)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_measurements_with_endtime_equals_starttime(self):
|
||||
query_parms = '?name=' + str(self._names_list[0]) + \
|
||||
'&merge_metrics=true' \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._start_time)
|
||||
self.assertRaises(exceptions.BadRequest,
|
||||
self.monasca_client.list_measurements, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_offset_limit(self):
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&merge_metrics=true&start_time=' + self._start_time + \
|
||||
'&end_time=' + self._end_time
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self._verify_list_measurements(resp, response_body)
|
||||
elements = response_body['elements']
|
||||
self._verify_list_measurements_elements(elements=elements,
|
||||
test_key=None, test_value=None)
|
||||
measurements = elements[0]['measurements']
|
||||
self._verify_list_measurements_meas_len(measurements=measurements,
|
||||
test_len=4)
|
||||
|
||||
for measurement_index in range(1, len(measurements) - 3):
|
||||
max_limit = len(measurements) - measurement_index
|
||||
|
||||
# Get first offset from api
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&merge_metrics=true&start_time=' + \
|
||||
measurements[measurement_index - 1][0] + \
|
||||
'&end_time=' + self._end_time + \
|
||||
'&limit=1'
|
||||
resp, response_body = self.monasca_client.list_measurements(query_parms)
|
||||
for link in response_body['links']:
|
||||
if link['rel'] == 'next':
|
||||
next_link = link['href']
|
||||
if not next_link:
|
||||
self.fail("No next link returned with query parameters: {}".formet(query_parms))
|
||||
offset = helpers.get_query_param(next_link, "offset")
|
||||
|
||||
first_index = measurement_index + 1
|
||||
|
||||
for limit in range(1, max_limit):
|
||||
last_index = measurement_index + limit + 1
|
||||
expected_measurements = measurements[first_index:last_index]
|
||||
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&merge_metrics=true&start_time=' + \
|
||||
self._start_time + '&end_time=' + \
|
||||
self._end_time + '&limit=' + str(limit) + \
|
||||
'&offset=' + str(offset)
|
||||
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self._verify_list_measurements(resp, response_body)
|
||||
new_measurements = response_body['elements'][0]['measurements']
|
||||
|
||||
self.assertEqual(limit, len(new_measurements))
|
||||
for i in range(len(expected_measurements)):
|
||||
self.assertEqual(expected_measurements[i],
|
||||
new_measurements[i])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_merge_metrics(self):
|
||||
query_parms = '?name=' + str(self._names_list[0]) + \
|
||||
'&merge_metrics=true' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_group_by_one(self):
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&group_by=key2' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 2)
|
||||
self._verify_list_measurements_elements(elements, None, None)
|
||||
for measurements in elements:
|
||||
self.assertEqual(1, len(measurements['dimensions'].keys()))
|
||||
self.assertEqual([u'key2'], list(measurements['dimensions'].keys()))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_group_by_multiple(self):
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&group_by=key2,key3' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 3)
|
||||
self._verify_list_measurements_elements(elements, None, None)
|
||||
for measurements in elements:
|
||||
self.assertEqual(2, len(measurements['dimensions'].keys()))
|
||||
self.assertEqual({u'key2', u'key3'}, set(measurements['dimensions'].keys()))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_group_by_all(self):
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&group_by=*' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 4)
|
||||
self._verify_list_measurements_elements(elements, None, None)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_group_by_and_merge(self):
|
||||
query_parms = '?name=' + str(self._names_list[1]) + \
|
||||
'&group_by=*' + \
|
||||
'&merge_metrics=true' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
resp, response_body = self.monasca_client.list_measurements(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 4)
|
||||
self._verify_list_measurements_elements(elements, None, None)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_measurements_with_name_exceeds_max_length(self):
|
||||
long_name = "x" * (constants.MAX_LIST_MEASUREMENTS_NAME_LENGTH + 1)
|
||||
query_parms = '?name=' + str(long_name) + '&merge_metrics=true' + \
|
||||
'&start_time=' + str(self._start_time) + \
|
||||
'&end_time=' + str(self._end_time)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_measurements, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_measurements_with_no_merge_metrics(self):
|
||||
query_parms = '?name=' + str(self._names_list[0]) + \
|
||||
'&start_time=' + str(self._start_time) + '&end_time=' \
|
||||
+ str(self._end_time)
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.monasca_client.list_measurements, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_with_duplicate_query_param_merges_positive(
|
||||
self):
|
||||
queries = []
|
||||
queries.append('?name={}&merge_metrics=true&start_time={}&end_time={'
|
||||
'}&merge_metrics=true'.
|
||||
format(self._names_list[0], self._start_time,
|
||||
self._end_time))
|
||||
queries.append('?name={}&merge_metrics=true&start_time={}&end_time={'
|
||||
'}&merge_metrics=false'.
|
||||
format(self._names_list[0], self._start_time,
|
||||
self._end_time))
|
||||
responses = list(map(self.monasca_client.list_measurements, queries))
|
||||
for i in range(2):
|
||||
self._verify_list_measurements(responses[i][0], responses[i][1])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_measurements_with_duplicate_query_param_merges_negative(
|
||||
self):
|
||||
queries = []
|
||||
queries.append('?name={}&merge_metrics=false&start_time={}&end_time={'
|
||||
'}&merge_metrics=true'.
|
||||
format(self._names_list[0], self._start_time,
|
||||
self._end_time))
|
||||
queries.append('?name={}&merge_metrics=false&start_time={}&end_time={'
|
||||
'}&merge_metrics=false'.
|
||||
format(self._names_list[0], self._start_time,
|
||||
self._end_time))
|
||||
for i in range(2):
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.monasca_client.list_measurements,
|
||||
queries[i])
|
||||
|
||||
def _verify_list_measurements_measurement(self, measurement, test_value):
|
||||
self.assertEqual(test_value, float(measurement[1]))
|
||||
|
||||
def _verify_list_measurements(self, resp, response_body):
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
|
||||
def _verify_list_measurements_elements(self, elements, test_key,
|
||||
test_value):
|
||||
if not elements:
|
||||
error_msg = "Failed: at least one element is needed. " \
|
||||
"Number of element = 0."
|
||||
self.fail(error_msg)
|
||||
|
||||
for element in elements:
|
||||
# element = elements[0]
|
||||
self.assertEqual(set(element),
|
||||
set(['columns', 'dimensions', 'id',
|
||||
'measurements', 'name']))
|
||||
self.assertTrue(type(element['name']) is str)
|
||||
self.assertTrue(type(element['dimensions']) is dict)
|
||||
self.assertTrue(type(element['columns']) is list)
|
||||
self.assertTrue(type(element['measurements']) is list)
|
||||
self.assertEqual(set(element['columns']),
|
||||
set(['timestamp', 'value', 'value_meta']))
|
||||
self.assertTrue(str(element['id']) is not None)
|
||||
if test_key is not None and test_value is not None:
|
||||
self.assertEqual(str(element['dimensions'][test_key]),
|
||||
test_value)
|
||||
|
||||
def _verify_list_measurements_meas_len(self, measurements, test_len):
|
||||
if measurements:
|
||||
len_measurements = len(measurements)
|
||||
self.assertEqual(len_measurements, test_len)
|
||||
else:
|
||||
error_msg = "Failed: one specific measurement is needed. " \
|
||||
"Number of measurements = 0"
|
||||
self.fail(error_msg)
|
||||
@@ -1,712 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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.
|
||||
|
||||
# TODO(RMH): Check if ' should be added in the list of INVALID_CHARS.
|
||||
# TODO(RMH): test_create_metric_no_value, should return 422 if value not sent
|
||||
import time
|
||||
from urllib import parse as urlparse
|
||||
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
|
||||
|
||||
class TestMetrics(base.BaseMonascaTest):
|
||||
|
||||
@decorators.attr(type=["gate", "smoke"])
|
||||
def test_create_metric(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value = data_utils.rand_name(u'value')
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
time_iso = helpers.timestamp_to_iso(timestamp)
|
||||
end_timestamp = int(round((time.time() + 3600 * 24) * 1000))
|
||||
end_time_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
value_meta_key = data_utils.rand_name(u'value_meta_key')
|
||||
value_meta_value = data_utils.rand_name(u'value_meta_value')
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={key: value},
|
||||
timestamp=timestamp,
|
||||
value=1.23,
|
||||
value_meta={
|
||||
value_meta_key: value_meta_value
|
||||
})
|
||||
resp, response_body = self.monasca_client.create_metrics(metric)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?name=' + name + '&start_time=' + time_iso + \
|
||||
'&end_time=' + end_time_iso
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_measurements(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
# check if metric is there and dimension info already available
|
||||
if element['name'] == name and len(element['dimensions']) > 0:
|
||||
self._verify_list_measurements_element(element, key, value)
|
||||
measurement = element['measurements'][0]
|
||||
self._verify_list_measurements_measurement(
|
||||
measurement, metric, value_meta_key, value_meta_value)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_create_metric: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_create_metric_with_multibyte_character(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value = data_utils.rand_name(u'value')
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
time_iso = helpers.timestamp_to_iso(timestamp)
|
||||
end_timestamp = int(round((time.time() + 3600 * 24) * 1000))
|
||||
end_time_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
value_meta_key = data_utils.rand_name(u'value_meta_key')
|
||||
value_meta_value = data_utils.rand_name(u'value_meta_value')
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={key: value},
|
||||
timestamp=timestamp,
|
||||
value=1.23,
|
||||
value_meta={
|
||||
value_meta_key: value_meta_value
|
||||
})
|
||||
resp, response_body = self.monasca_client.create_metrics(metric)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?name=' + urlparse.quote(name.encode('utf8')) + \
|
||||
'&start_time=' + time_iso + '&end_time=' + end_time_iso
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_measurements(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if element['name'] == name:
|
||||
self._verify_list_measurements_element(element, key, value)
|
||||
measurement = element['measurements'][0]
|
||||
self._verify_list_measurements_measurement(
|
||||
measurement, metric, value_meta_key, value_meta_value)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_create_metric: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_create_metrics(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value = data_utils.rand_name(u'value')
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
time_iso = helpers.timestamp_to_iso(timestamp)
|
||||
end_timestamp = int(round(timestamp + 3600 * 24 * 1000))
|
||||
end_time_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
value_meta_key1 = data_utils.rand_name(u'meta_key')
|
||||
value_meta_value1 = data_utils.rand_name(u'meta_value')
|
||||
value_meta_key2 = data_utils.rand_name(u'value_meta_key')
|
||||
value_meta_value2 = data_utils.rand_name(u'value_meta_value')
|
||||
metrics = [
|
||||
helpers.create_metric(name=name,
|
||||
dimensions={key: value},
|
||||
timestamp=timestamp,
|
||||
value=1.23,
|
||||
value_meta={
|
||||
value_meta_key1: value_meta_value1
|
||||
}),
|
||||
helpers.create_metric(name=name,
|
||||
dimensions={key: value},
|
||||
timestamp=timestamp + 6000,
|
||||
value=4.56,
|
||||
value_meta={
|
||||
value_meta_key2: value_meta_value2
|
||||
})
|
||||
]
|
||||
resp, response_body = self.monasca_client.create_metrics(metrics)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?name=' + name + '&start_time=' + str(time_iso) + \
|
||||
'&end_time=' + str(end_time_iso)
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_measurements(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if (element['name'] == name and
|
||||
len(element['measurements']) == 2):
|
||||
self._verify_list_measurements_element(element, key, value)
|
||||
first_measurement = element['measurements'][0]
|
||||
second_measurement = element['measurements'][1]
|
||||
self._verify_list_measurements_measurement(
|
||||
first_measurement, metrics[0], value_meta_key1,
|
||||
value_meta_value1)
|
||||
self._verify_list_measurements_measurement(
|
||||
second_measurement, metrics[1], value_meta_key2,
|
||||
value_meta_value2)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_create_metrics: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_no_name(self):
|
||||
metric = helpers.create_metric(name=None)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_empty_name(self):
|
||||
metric = helpers.create_metric(name='')
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_empty_value_in_dimensions(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={u'key': ''})
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_empty_key_in_dimensions(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={'': u'value'})
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_create_metric_with_no_dimensions(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
time_iso = helpers.timestamp_to_iso(timestamp)
|
||||
end_timestamp = int(round(timestamp + 3600 * 24 * 1000))
|
||||
end_time_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
value_meta_key = data_utils.rand_name(u'value_meta_key')
|
||||
value_meta_value = data_utils.rand_name(u'value_meta_value')
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions=None,
|
||||
timestamp=timestamp,
|
||||
value=1.23,
|
||||
value_meta={
|
||||
value_meta_key: value_meta_value})
|
||||
resp, response_body = self.monasca_client.create_metrics(metric)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?name=' + str(name) + '&start_time=' + str(time_iso) \
|
||||
+ '&end_time=' + str(end_time_iso)
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_measurements(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if element['name'] == name:
|
||||
self._verify_list_measurements_element(
|
||||
element, test_key=None, test_value=None)
|
||||
if len(element['measurements']) > 0:
|
||||
measurement = element['measurements'][0]
|
||||
self._verify_list_measurements_measurement(
|
||||
measurement, metric, value_meta_key,
|
||||
value_meta_value)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_create_metric_with_no_dimensions: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_create_metric_with_colon_in_dimension_value(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = u'url'
|
||||
value = u'http://localhost:8070/v2.0'
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
time_iso = helpers.timestamp_to_iso(timestamp)
|
||||
end_timestamp = int(round((time.time() + 3600 * 24) * 1000))
|
||||
end_time_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={key: value})
|
||||
resp, response_body = self.monasca_client.create_metrics(metric)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?name=' + name + '&start_time=' + time_iso + \
|
||||
'&end_time=' + end_time_iso + \
|
||||
'&dimensions=' + key + ':' + value
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client. \
|
||||
list_measurements(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if element['name'] == name:
|
||||
self._verify_list_measurements_element(element, key, value)
|
||||
measurement = element['measurements'][0]
|
||||
self._verify_list_measurements_measurement(
|
||||
measurement, metric, None, None)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_create_metric: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_no_timestamp(self):
|
||||
metric = helpers.create_metric()
|
||||
metric['timestamp'] = None
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_no_value(self):
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
metric = helpers.create_metric(timestamp=timestamp,
|
||||
value=None)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_name_exceeds_max_length(self):
|
||||
long_name = u"x" * (constants.MAX_METRIC_NAME_LENGTH + 1)
|
||||
metric = helpers.create_metric(long_name)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_invalid_chars_in_name(self):
|
||||
for invalid_char in constants.INVALID_NAME_CHARS:
|
||||
metric = helpers.create_metric(invalid_char)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_invalid_chars_in_dimensions(self):
|
||||
for invalid_char in constants.INVALID_DIMENSION_CHARS:
|
||||
metric = helpers.create_metric(u'name-1', {u'key-1': invalid_char})
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
for invalid_char in constants.INVALID_DIMENSION_CHARS:
|
||||
metric = helpers.create_metric(u'name-1',
|
||||
{invalid_char: u'value-1'})
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_dimension_key_exceeds_max_length(self):
|
||||
long_key = u"x" * (constants.MAX_DIMENSION_KEY_LENGTH + 1)
|
||||
metric = helpers.create_metric(u'name-1', {long_key: u'value-1'})
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_dimension_value_exceeds_max_length(self):
|
||||
long_value = u"x" * (constants.MAX_DIMENSION_VALUE_LENGTH + 1)
|
||||
metric = helpers.create_metric(u'name-1', {u'key-1': long_value})
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_value_meta_name_exceeds_max_length(self):
|
||||
long_value_meta_name = u"x" * (constants.MAX_VALUE_META_NAME_LENGTH + 1)
|
||||
value_meta_dict = {long_value_meta_name: u"value_meta_value"}
|
||||
metric = helpers.create_metric(name=u'name', value_meta=value_meta_dict)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_with_value_meta_exceeds_max_length(self):
|
||||
value_meta_name = u"x"
|
||||
long_value_meta_value = u"y" * constants.MAX_VALUE_META_TOTAL_LENGTH
|
||||
value_meta_dict = {value_meta_name: long_value_meta_value}
|
||||
metric = helpers.create_metric(name=u'name', value_meta=value_meta_dict)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@decorators.attr(type=["gate", "smoke"])
|
||||
def test_list_metrics(self):
|
||||
resp, response_body = self.monasca_client.list_metrics()
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
elements = response_body['elements']
|
||||
element = elements[0]
|
||||
self._verify_list_metrics_element(element, test_key=None,
|
||||
test_value=None, test_name=None)
|
||||
self.assertTrue(set(['id', 'name', 'dimensions']) == set(element))
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_with_dimensions(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value = data_utils.rand_name(u'value')
|
||||
metric = helpers.create_metric(name=name, dimensions={key: value})
|
||||
resp, response_body = self.monasca_client.create_metrics(metric)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?dimensions=' + key + ':' + value
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.list_metrics(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if element['dimensions'][key] == value:
|
||||
self._verify_list_metrics_element(element, test_name=name)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_list_metrics_with_dimensions: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_dimension_query_multi_value_with_diff_names(self):
|
||||
metrics, name, key_service, values = \
|
||||
self._create_metrics_with_different_dimensions(same_name=False)
|
||||
metric_dimensions = self._get_metric_dimensions(
|
||||
key_service, values, same_metric_name=False)
|
||||
query_param = '?dimensions=' + key_service + ':' + values[0] + '|' +\
|
||||
values[1]
|
||||
self._verify_dimensions(query_param, metric_dimensions)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_dimension_query_no_value_with_diff_names(self):
|
||||
metrics, name, key_service, values = \
|
||||
self._create_metrics_with_different_dimensions(same_name=False)
|
||||
metric_dimensions = self._get_metric_dimensions(
|
||||
key_service, values, same_metric_name=False)
|
||||
query_param = '?dimensions=' + key_service
|
||||
self._verify_dimensions(query_param, metric_dimensions)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_dimension_query_multi_value_with_same_name(self):
|
||||
# Skip the test for now due to InfluxDB Inconsistency
|
||||
return
|
||||
metrics, name, key_service, values = \
|
||||
self._create_metrics_with_different_dimensions(same_name=True)
|
||||
metric_dimensions = self._get_metric_dimensions(
|
||||
key_service, values, same_metric_name=True)
|
||||
query_param = '?name=' + name + '&dimensions=' + key_service + ':' +\
|
||||
values[0] + '|' + values[1]
|
||||
self._verify_dimensions(query_param, metric_dimensions)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_dimension_query_no_value_with_same_name(self):
|
||||
# Skip the test for now due to InfluxDB Inconsistency
|
||||
return
|
||||
metrics, name, key_service, values = \
|
||||
self._create_metrics_with_different_dimensions(same_name=True)
|
||||
metric_dimensions = self._get_metric_dimensions(
|
||||
key_service, values, same_metric_name=True)
|
||||
query_param = '?name=' + name + '&dimensions=' + key_service
|
||||
self._verify_dimensions(query_param, metric_dimensions)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_with_name(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value = data_utils.rand_name(u'value')
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={key: value})
|
||||
resp, response_body = self.monasca_client.create_metrics(metric)
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?name=' + str(name)
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.list_metrics(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if element['name'] == name:
|
||||
self._verify_list_metrics_element(element, test_key=key,
|
||||
test_value=value)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_list_metrics_with_name: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_with_project(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value = data_utils.rand_name(u'value')
|
||||
project = self.projects_client.create_project(
|
||||
name=data_utils.rand_name(u'test_project'))['project']
|
||||
# Delete the project at the end of the test
|
||||
self.addCleanup(self.projects_client.delete_project, project['id'])
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={key: value})
|
||||
resp, response_body = self.monasca_client.create_metrics(
|
||||
metric, tenant_id=project['id'])
|
||||
self.assertEqual(204, resp.status)
|
||||
query_param = '?tenant_id=' + str(project['id'])
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.list_metrics(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if element['name'] == name:
|
||||
self._verify_list_metrics_element(element, test_key=key,
|
||||
test_value=value)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Failed test_list_metrics_with_tenant: " \
|
||||
"timeout on waiting for metrics: at least " \
|
||||
"one metric is needed. Current number of " \
|
||||
"metrics = 0"
|
||||
self.fail(error_msg)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_with_offset_limit(self):
|
||||
name = data_utils.rand_name()
|
||||
key1 = data_utils.rand_name()
|
||||
key2 = data_utils.rand_name()
|
||||
|
||||
metrics = [
|
||||
helpers.create_metric(name=name, dimensions={
|
||||
key1: u'value-1', key2: u'value-1'}),
|
||||
helpers.create_metric(name=name, dimensions={
|
||||
key1: u'value-2', key2: u'value-2'}),
|
||||
helpers.create_metric(name=name, dimensions={
|
||||
key1: u'value-3', key2: u'value-3'}),
|
||||
helpers.create_metric(name=name, dimensions={
|
||||
key1: u'value-4', key2: u'value-4'})
|
||||
]
|
||||
self.monasca_client.create_metrics(metrics)
|
||||
query_param = '?name=' + name
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.list_metrics(query_param)
|
||||
elements = response_body['elements']
|
||||
if elements and len(elements) == 4:
|
||||
break
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = ("Failed test_list_metrics_with_offset_limit: "
|
||||
"timeout on waiting for metrics: 4 metrics "
|
||||
"are needed. Current number of elements = "
|
||||
"{}").format(len(elements))
|
||||
self.fail(error_msg)
|
||||
|
||||
first_element = elements[0]
|
||||
query_parms = '?name=' + name + '&limit=4'
|
||||
resp, response_body = self.monasca_client.list_metrics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(4, len(elements))
|
||||
self.assertEqual(first_element, elements[0])
|
||||
|
||||
for metric_index in range(len(elements) - 1):
|
||||
metric = elements[metric_index]
|
||||
max_limit = 3 - metric_index
|
||||
|
||||
for limit in range(1, max_limit):
|
||||
first_index = metric_index + 1
|
||||
last_index = first_index + limit
|
||||
expected_elements = elements[first_index:last_index]
|
||||
|
||||
query_parms = '?name=' + name + '&offset=' + \
|
||||
str(metric['id']) + '&limit=' + \
|
||||
str(limit)
|
||||
resp, response_body = self.\
|
||||
monasca_client.list_metrics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
new_elements = response_body['elements']
|
||||
|
||||
self.assertEqual(limit, len(new_elements))
|
||||
for i in range(len(expected_elements)):
|
||||
self.assertEqual(expected_elements[i], new_elements[i])
|
||||
|
||||
def _verify_list_measurements_element(self, element, test_key, test_value):
|
||||
self.assertEqual(set(element),
|
||||
set(['columns', 'dimensions', 'id', 'measurements',
|
||||
'name']))
|
||||
self.assertEqual(set(element['columns']),
|
||||
set(['timestamp', 'value', 'value_meta']))
|
||||
self.assertTrue(str(element['id']) is not None)
|
||||
if test_key is not None and test_value is not None:
|
||||
self.assertEqual(
|
||||
element['dimensions'][test_key].encode('utf-8'),
|
||||
test_value.encode('utf-8')
|
||||
)
|
||||
|
||||
def _verify_list_measurements_measurement(self, measurement,
|
||||
test_metric, test_vm_key,
|
||||
test_vm_value):
|
||||
# Timestamps stored in influx sometimes are 1 millisecond different to
|
||||
# the value stored by the persister. Check if the timestamps are
|
||||
# equal in one millisecond range to pass the test.
|
||||
time_iso_millis = helpers.timestamp_to_iso_millis(
|
||||
test_metric['timestamp'] + 0)
|
||||
time_iso_millis_plus = helpers.timestamp_to_iso_millis(
|
||||
test_metric['timestamp'] + 1)
|
||||
time_iso_millis_minus = helpers.timestamp_to_iso_millis(
|
||||
test_metric['timestamp'] - 1)
|
||||
if str(measurement[0]) != time_iso_millis and str(measurement[0]) != \
|
||||
time_iso_millis_plus and str(measurement[0]) != \
|
||||
time_iso_millis_minus:
|
||||
error_msg = ("Mismatch Error: None of {}, {}, {} matches {}").\
|
||||
format(time_iso_millis, time_iso_millis_plus,
|
||||
time_iso_millis_minus, str(measurement[0]))
|
||||
self.fail(error_msg)
|
||||
self.assertEqual(measurement[1], test_metric['value'])
|
||||
if test_vm_key is not None and test_vm_value is not None:
|
||||
self.assertEqual(
|
||||
measurement[2][test_vm_key].encode('utf-8'),
|
||||
test_vm_value.encode('utf-8')
|
||||
)
|
||||
|
||||
def _verify_list_metrics_element(self, element, test_key=None,
|
||||
test_value=None, test_name=None):
|
||||
self.assertTrue(type(element['id']) is str)
|
||||
self.assertTrue(type(element['name']) is str)
|
||||
self.assertTrue(type(element['dimensions']) is dict)
|
||||
self.assertEqual(set(element), set(['dimensions', 'id', 'name']))
|
||||
self.assertTrue(str(element['id']) is not None)
|
||||
if test_key is not None and test_value is not None:
|
||||
self.assertEqual(element['dimensions'][test_key], test_value)
|
||||
if test_name is not None:
|
||||
self.assertEqual(element['name'], test_name)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_with_time_args(self):
|
||||
name = data_utils.rand_name(u'name')
|
||||
key = data_utils.rand_name(u'key')
|
||||
value_org = data_utils.rand_name(u'value')
|
||||
|
||||
now = int(round(time.time() * 1000))
|
||||
#
|
||||
# Built start and end time args before and after the measurement.
|
||||
#
|
||||
start_iso = helpers.timestamp_to_iso(now - 1000)
|
||||
end_timestamp = int(round(now + 1000))
|
||||
end_iso = helpers.timestamp_to_iso(end_timestamp)
|
||||
|
||||
metric = helpers.create_metric(name=name,
|
||||
dimensions={key: value_org},
|
||||
timestamp=now)
|
||||
|
||||
self.monasca_client.create_metrics(metric)
|
||||
for timer in range(constants.MAX_RETRIES):
|
||||
query_parms = '?name=' + name + '&start_time=' + start_iso + '&end_time=' + end_iso
|
||||
resp, response_body = self.monasca_client.list_metrics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
if elements:
|
||||
dimensions = elements[0]
|
||||
dimension = dimensions['dimensions']
|
||||
value = dimension[str(key)]
|
||||
self.assertEqual(value_org, value)
|
||||
break
|
||||
else:
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if timer == constants.MAX_RETRIES - 1:
|
||||
skip_msg = "Skipped test_list_metrics_with_time_args: " \
|
||||
"timeout on waiting for metrics: at least one " \
|
||||
"metric is needed. Current number of metrics " \
|
||||
"= 0"
|
||||
raise self.skipException(skip_msg)
|
||||
|
||||
@staticmethod
|
||||
def _get_metric_dimensions(key_service, values, same_metric_name):
|
||||
if same_metric_name:
|
||||
metric_dimensions = [{key_service: values[0], 'key3': ''},
|
||||
{key_service: values[1], 'key3': ''},
|
||||
{key_service: '', 'key3': 'value3'}]
|
||||
else:
|
||||
metric_dimensions = [{key_service: values[0]},
|
||||
{key_service: values[1]},
|
||||
{'key3': 'value3'}]
|
||||
return metric_dimensions
|
||||
|
||||
def _verify_dimensions(self, query_param, metric_dimensions):
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.list_metrics(query_param)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
if len(elements) == 2:
|
||||
dimension_sets = []
|
||||
for element in elements:
|
||||
dimension_sets.append(element['dimensions'])
|
||||
self.assertIn(metric_dimensions[0], dimension_sets)
|
||||
self.assertIn(metric_dimensions[1], dimension_sets)
|
||||
self.assertNotIn(metric_dimensions[2], dimension_sets)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
if i == constants.MAX_RETRIES - 1:
|
||||
error_msg = "Timeout on waiting for metrics: at least " \
|
||||
"2 metrics are needed. Current number of " \
|
||||
"metrics = {}".format(len(elements))
|
||||
self.fail(error_msg)
|
||||
|
||||
def _create_metrics_with_different_dimensions(self, same_name=True):
|
||||
name1 = data_utils.rand_name(u'name1')
|
||||
name2 = name1 if same_name else data_utils.rand_name(u'name2')
|
||||
name3 = name1 if same_name else data_utils.rand_name(u'name3')
|
||||
key_service = data_utils.rand_name(u'service')
|
||||
values = [data_utils.rand_name(u'value1'),
|
||||
data_utils.rand_name(u'value2')]
|
||||
metrics = [helpers.create_metric(name1, {key_service: values[0]}),
|
||||
helpers.create_metric(name2, {key_service: values[1]}),
|
||||
helpers.create_metric(name3, {u'key3': u'value3'})]
|
||||
resp, response_body = self.monasca_client.create_metrics(metrics)
|
||||
self.assertEqual(204, resp.status)
|
||||
return metrics, name1, key_service, values
|
||||
@@ -1,162 +0,0 @@
|
||||
# (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 urllib.parse import urlencode
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class TestMetricsNames(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestMetricsNames, cls).resource_setup()
|
||||
name1 = data_utils.rand_name('name1')
|
||||
name2 = data_utils.rand_name('name2')
|
||||
name3 = data_utils.rand_name('name3')
|
||||
key = data_utils.rand_name()
|
||||
key1 = data_utils.rand_name()
|
||||
value = data_utils.rand_name()
|
||||
value1 = data_utils.rand_name()
|
||||
|
||||
timestamp = int(round(time.time() * 1000))
|
||||
time_iso = helpers.timestamp_to_iso(timestamp)
|
||||
|
||||
metric1 = helpers.create_metric(name=name1,
|
||||
dimensions={key: value})
|
||||
metric2 = helpers.create_metric(name=name2,
|
||||
dimensions={key1: value1})
|
||||
metric3 = helpers.create_metric(name=name3,
|
||||
dimensions={key: value})
|
||||
cls._test_metric_names = {name1, name2, name3}
|
||||
cls._expected_names_list = list(cls._test_metric_names)
|
||||
cls._expected_names_list.sort()
|
||||
cls._test_metric_names_with_same_dim = [name1, name3]
|
||||
cls._test_metrics = [metric1, metric2, metric3]
|
||||
cls._dimensions_param = key + ':' + value
|
||||
|
||||
cls.monasca_client.create_metrics(cls._test_metrics)
|
||||
|
||||
query_param = '?start_time=' + time_iso
|
||||
returned_name_set = set()
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = cls.monasca_client.list_metrics(query_param)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
returned_name_set.add(str(element['name']))
|
||||
if cls._test_metric_names.issubset(returned_name_set):
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
|
||||
assert False, 'Unable to initialize metrics'
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestMetricsNames, cls).resource_cleanup()
|
||||
|
||||
@decorators.attr(type=["gate", "smoke"])
|
||||
def test_list_metrics_names(self):
|
||||
resp, response_body = self.monasca_client.list_metrics_names()
|
||||
metric_names = self._verify_response(resp, response_body)
|
||||
self.assertEqual(metric_names, self._expected_names_list)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_names_with_dimensions(self):
|
||||
query_params = '?dimensions=' + self._dimensions_param
|
||||
resp, response_body = self.monasca_client.list_metrics_names(
|
||||
query_params)
|
||||
metric_names = self._verify_response(resp, response_body)
|
||||
self.assertEqual(metric_names,
|
||||
self._test_metric_names_with_same_dim)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_names_with_limit_offset(self):
|
||||
resp, response_body = self.monasca_client.list_metrics_names()
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
num_names = len(elements)
|
||||
|
||||
for limit in range(1, num_names):
|
||||
start_index = 0
|
||||
params = [('limit', limit)]
|
||||
offset = None
|
||||
while True:
|
||||
num_expected_elements = limit
|
||||
if (num_expected_elements + start_index) > num_names:
|
||||
num_expected_elements = num_names - start_index
|
||||
|
||||
these_params = list(params)
|
||||
# If not the first call, use the offset returned by the last
|
||||
# call
|
||||
if offset:
|
||||
these_params.extend([('offset', str(offset))])
|
||||
query_params = '?' + urlencode(these_params)
|
||||
|
||||
resp, response_body = \
|
||||
self.monasca_client.list_metrics_names(query_params)
|
||||
new_elements = self._verify_response(resp, response_body)
|
||||
self.assertEqual(num_expected_elements, len(new_elements))
|
||||
|
||||
expected_elements = elements[start_index:start_index + limit]
|
||||
expected_names = \
|
||||
[expected_elements[i]['name'] for i in range(
|
||||
len(expected_elements))]
|
||||
|
||||
self.assertEqual(expected_names, new_elements)
|
||||
start_index += num_expected_elements
|
||||
if start_index >= num_names:
|
||||
break
|
||||
# Get the next set
|
||||
offset = self._get_offset(response_body)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_list_metrics_names_with_offset_not_in_metrics_names_list(self):
|
||||
offset1 = 'tempest-abc'
|
||||
offset2 = 'tempest-name111'
|
||||
offset3 = 'tempest-name4-random'
|
||||
query_param1 = '?' + urlencode([('offset', offset1)])
|
||||
query_param2 = '?' + urlencode([('offset', offset2)])
|
||||
query_param3 = '?' + urlencode([('offset', offset3)])
|
||||
|
||||
resp, response_body = self.monasca_client.list_metrics_names(
|
||||
query_param1)
|
||||
metric_names = self._verify_response(resp, response_body)
|
||||
|
||||
self.assertEqual(metric_names, self._expected_names_list[:])
|
||||
|
||||
resp, response_body = self.monasca_client.list_metrics_names(
|
||||
query_param2)
|
||||
metric_names = self._verify_response(resp, response_body)
|
||||
self.assertEqual(metric_names, self._expected_names_list[1:])
|
||||
|
||||
resp, response_body = self.monasca_client.list_metrics_names(
|
||||
query_param3)
|
||||
self.assertEqual(response_body['elements'], [])
|
||||
|
||||
def _verify_response(self, resp, response_body):
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
|
||||
response_names_length = len(response_body['elements'])
|
||||
if response_names_length == 0:
|
||||
self.fail("No metric names returned")
|
||||
|
||||
metric_names = [str(response_body['elements'][i]['name']) for i in
|
||||
range(response_names_length)]
|
||||
return metric_names
|
||||
@@ -1,33 +0,0 @@
|
||||
# (C) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 monasca_tempest_tests.tests.api import base
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class TestNotificationMethodType(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestNotificationMethodType, cls).resource_setup()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestNotificationMethodType, cls).resource_cleanup()
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_notification_method_type(self):
|
||||
resp, response_body = (self.monasca_client.
|
||||
list_notification_method_types())
|
||||
self.assertEqual(200, resp.status)
|
||||
@@ -1,953 +0,0 @@
|
||||
# (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# 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 urllib import parse as urlparse
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
DEFAULT_EMAIL_ADDRESS = 'john.doe@domain.com'
|
||||
|
||||
|
||||
class TestNotificationMethods(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestNotificationMethods, cls).resource_setup()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestNotificationMethods, cls).resource_cleanup()
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_notification_method(self):
|
||||
notification = helpers.create_notification()
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_email_notification_method_with_lower_case_type(self):
|
||||
notification = helpers.create_notification(name='lower case email notification',
|
||||
type='email')
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_email_notification_method_with_mixed_case_type(self):
|
||||
notification = helpers.create_notification(name='mixed case email notification',
|
||||
type='EmAil')
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_notification_method_period_not_defined(self):
|
||||
notification = helpers.create_notification(period=None)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_webhook_notification_method_with_non_zero_period(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name,
|
||||
type='WEBHOOK',
|
||||
address='http://localhost/test01',
|
||||
period=60)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_notification_method_webhook_test_tld(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name,
|
||||
type='WEBHOOK',
|
||||
address='http://mytest.test/webhook',
|
||||
period=60)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_notification_method_webhook_test_tld_and_port(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name,
|
||||
type='WEBHOOK',
|
||||
address='http://mytest.test:4533/webhook',
|
||||
period=60)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_no_name(self):
|
||||
notification = helpers.create_notification(name=None)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_no_type(self):
|
||||
notification = helpers.create_notification(type=None)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_no_address(self):
|
||||
notification = helpers.create_notification(address=None)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_name_exceeds_max_length(self):
|
||||
long_name = "x" * (constants.MAX_NOTIFICATION_METHOD_NAME_LENGTH + 1)
|
||||
notification = helpers.create_notification(name=long_name)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_address_exceeds_max_length(self):
|
||||
long_address = "x" * (
|
||||
constants.MAX_NOTIFICATION_METHOD_ADDRESS_LENGTH + 1)
|
||||
notification = helpers.create_notification(address=long_address)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_email_address(self):
|
||||
notification = helpers.create_notification(address="name@")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_scheme_webhook(self):
|
||||
notification = helpers.create_notification(type="WEBHOOK",
|
||||
address="ftp://localhost")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_webhook_address(self):
|
||||
notification = helpers.create_notification(type="WEBHOOK",
|
||||
address="localhost:123")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
# The below tests are making sure that we accept passing in case insensitive types
|
||||
# and that we still validate the
|
||||
# address if the types are case insensitive
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_notification_method_webhook_with_lower_case_type(self):
|
||||
notification = helpers.create_notification(type='webhook',
|
||||
address='http://mytest.test:4533')
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_notification_method_webhook_with_mixed_case_type(self):
|
||||
notification = helpers.create_notification(type='webHooK',
|
||||
address='http://mytest.test:4533')
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_email_address_type_all_lower_case(self):
|
||||
notification = helpers.create_notification(type="email",
|
||||
address="name@")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_email_address_type_all_mixed_case(self):
|
||||
notification = helpers.create_notification(type="EmAil",
|
||||
address="name@")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_webhook_address_type_mixed_case(self):
|
||||
notification = helpers.create_notification(type="WebHook",
|
||||
address="localhost:123")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_webhook_address_type_lower_case(self):
|
||||
notification = helpers.create_notification(type="webhook",
|
||||
address="localhost:123")
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_type(self):
|
||||
notification = helpers.create_notification(type='random')
|
||||
self.assertRaises(
|
||||
(exceptions.BadRequest, exceptions.NotFound, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_float_period(self):
|
||||
notification = helpers.create_notification(period=1.2)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_method_with_invalid_string_period(self):
|
||||
notification = helpers.create_notification(period='random')
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_webhook_notification_method_with_invalid_period(self):
|
||||
notification = helpers.create_notification(type='WEBHOOK',
|
||||
address='http://localhost/test01',
|
||||
period=10)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.create_notifications,
|
||||
notification)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_notification_methods(self):
|
||||
notification = helpers.create_notification()
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
resp, response_body = self.monasca_client.list_notification_methods()
|
||||
self.assertEqual(200, resp.status)
|
||||
# Test response body
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
elements = response_body['elements']
|
||||
element = elements[0]
|
||||
self.assertTrue(set(['id', 'links', 'name', 'type', 'address', 'period']) ==
|
||||
set(element))
|
||||
self.assertTrue(type(element['id']) is str)
|
||||
self.assertTrue(type(element['links']) is list)
|
||||
self.assertTrue(type(element['name']) is str)
|
||||
self.assertTrue(type(element['type']) is str)
|
||||
self.assertTrue(type(element['address']) is str)
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_notification_methods_sort_by(self):
|
||||
notifications = [helpers.create_notification(
|
||||
name='notification sort by 01',
|
||||
type='PAGERDUTY',
|
||||
address='test03@localhost',
|
||||
), helpers.create_notification(
|
||||
name='notification sort by 02',
|
||||
type='WEBHOOK',
|
||||
address='http://localhost/test01',
|
||||
), helpers.create_notification(
|
||||
name='notification sort by 03',
|
||||
type='EMAIL',
|
||||
address='test02@localhost',
|
||||
)]
|
||||
for notification in notifications:
|
||||
resp, response_body = self.monasca_client.create_notifications(notification)
|
||||
notification['id'] = response_body['id']
|
||||
time.sleep(1)
|
||||
|
||||
sort_params1 = ['id', 'name', 'type', 'address']
|
||||
for sort_by in sort_params1:
|
||||
notif_sorted_by = sorted(notifications,
|
||||
key=lambda obj: obj[sort_by])
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + sort_by)
|
||||
self.assertEqual(200, resp.status)
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notif_sorted_by[i][sort_by], element[sort_by])
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + sort_by + urlparse.quote(' asc'))
|
||||
self.assertEqual(200, resp.status)
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notif_sorted_by[i][sort_by], element[sort_by])
|
||||
|
||||
notif_sorted_by_reverse = sorted(notifications,
|
||||
key=lambda obj: obj[sort_by],
|
||||
reverse=True)
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + sort_by + urlparse.quote(' desc'))
|
||||
self.assertEqual(200, resp.status)
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notif_sorted_by_reverse[i][sort_by], element[sort_by])
|
||||
|
||||
sort_params2 = ['created_at', 'updated_at']
|
||||
for sort_by in sort_params2:
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + sort_by)
|
||||
self.assertEqual(200, resp.status)
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notifications[i]['id'], element['id'])
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + sort_by + urlparse.quote(' asc'))
|
||||
self.assertEqual(200, resp.status)
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notifications[i]['id'], element['id'])
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + sort_by + urlparse.quote(' desc'))
|
||||
self.assertEqual(200, resp.status)
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notifications[-i - 1]['id'], element['id'])
|
||||
|
||||
for notification in notifications:
|
||||
self.monasca_client.delete_notification_method(notification['id'])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_notification_methods_multiple_sort_by(self):
|
||||
notifications = [helpers.create_notification(
|
||||
name='notification sort by 01',
|
||||
type='EMAIL',
|
||||
address='test02@localhost',
|
||||
), helpers.create_notification(
|
||||
name='notification sort by 02',
|
||||
type='PAGERDUTY',
|
||||
address='test03@localhost',
|
||||
), helpers.create_notification(
|
||||
name='notification sort by 03',
|
||||
type='EMAIL',
|
||||
address='test04@localhost',
|
||||
), helpers.create_notification(
|
||||
name='notification sort by 04',
|
||||
type='EMAIL',
|
||||
address='test01@localhost',
|
||||
)]
|
||||
for notification in notifications:
|
||||
resp, response_body = self.monasca_client.create_notifications(notification)
|
||||
notification['id'] = response_body['id']
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods(
|
||||
'?sort_by=' + urlparse.quote('type asc,address desc,id'))
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
expected_order = [2, 0, 3, 1]
|
||||
|
||||
for i, element in enumerate(response_body['elements']):
|
||||
self.assertEqual(notifications[expected_order[i]]['id'], element['id'])
|
||||
|
||||
for element in response_body['elements']:
|
||||
self.monasca_client.delete_notification_method(element['id'])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_notification_methods_invalid_sort_by(self):
|
||||
query_parms = '?sort_by=random'
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_notification_methods,
|
||||
query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_notification_methods_with_offset_limit(self):
|
||||
name1 = data_utils.rand_name('notification')
|
||||
name2 = data_utils.rand_name('notification')
|
||||
name3 = data_utils.rand_name('notification')
|
||||
name4 = data_utils.rand_name('notification')
|
||||
notification1 = helpers.create_notification(name=name1)
|
||||
notification2 = helpers.create_notification(name=name2)
|
||||
notification3 = helpers.create_notification(name=name3)
|
||||
notification4 = helpers.create_notification(name=name4)
|
||||
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification1)
|
||||
id1 = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification2)
|
||||
id2 = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification3)
|
||||
id3 = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification4)
|
||||
id4 = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
resp, response_body = self.monasca_client.list_notification_methods()
|
||||
elements = response_body['elements']
|
||||
|
||||
first_element = elements[0]
|
||||
last_element = elements[3]
|
||||
|
||||
query_parms = '?limit=4'
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_notification_methods(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(4, len(response_body['elements']))
|
||||
self.assertEqual(first_element, response_body['elements'][0])
|
||||
|
||||
timeout = time.time() + 60 * 1 # 1 minute timeout
|
||||
for limit in range(1, 5):
|
||||
next_element = elements[limit - 1]
|
||||
offset = limit
|
||||
while True:
|
||||
if time.time() < timeout:
|
||||
query_parms = '?offset=' + str(offset) + \
|
||||
'&limit=' + str(limit)
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_notification_methods(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
new_elements = response_body['elements']
|
||||
if len(new_elements) > limit - 1:
|
||||
self.assertEqual(limit, len(new_elements))
|
||||
next_element = new_elements[limit - 1]
|
||||
offset += 1
|
||||
elif 0 < len(new_elements) <= limit - 1:
|
||||
self.assertEqual(last_element, new_elements[0])
|
||||
break
|
||||
else:
|
||||
self.assertEqual(last_element, next_element)
|
||||
break
|
||||
else:
|
||||
msg = "Failed " \
|
||||
"test_list_notification_methods_with_offset_limit:" \
|
||||
" one minute timeout on offset limit test loop."
|
||||
raise exceptions.TimeoutException(msg)
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id1)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id2)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id3)
|
||||
self.assertEqual(204, resp.status)
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id4)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_get_notification_method(self):
|
||||
notification = helpers.create_notification()
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
resp, response_body = self.monasca_client.get_notification_method(id)
|
||||
self.assertEqual(200, resp.status)
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_get_notification_method_with_invalid_id(self):
|
||||
notification = helpers.create_notification()
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = data_utils.rand_name()
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.monasca_client.get_notification_method,
|
||||
id)
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(response_body['id'])
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_update_notification_method_name(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(name, response_body['name'])
|
||||
id = response_body['id']
|
||||
new_name = name + 'update'
|
||||
resp, response_body = self.monasca_client.\
|
||||
update_notification_method(id, new_name,
|
||||
type=response_body['type'],
|
||||
address=response_body['address'],
|
||||
period=response_body['period'])
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_name, response_body['name'])
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_update_notification_method_type(self):
|
||||
type = 'EMAIL'
|
||||
notification = helpers.create_notification(type=type)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(type, response_body['type'])
|
||||
id = response_body['id']
|
||||
new_type = 'PAGERDUTY'
|
||||
resp, response_body = \
|
||||
self.monasca_client.\
|
||||
update_notification_method(id, name=response_body['name'],
|
||||
type=new_type,
|
||||
address=response_body['address'],
|
||||
period=response_body['period'])
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_type, response_body['type'])
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_update_notification_method_address(self):
|
||||
address = DEFAULT_EMAIL_ADDRESS
|
||||
notification = helpers.create_notification(address=address)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(address, response_body['address'])
|
||||
id = response_body['id']
|
||||
new_address = 'jane.doe@domain.com'
|
||||
resp, response_body = self.monasca_client.\
|
||||
update_notification_method(id,
|
||||
name=response_body['name'],
|
||||
type=response_body['type'],
|
||||
address=new_address,
|
||||
period=0)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_address, response_body['address'])
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_notification_method_name_exceeds_max_length(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
new_name_long = "x" * (constants.MAX_NOTIFICATION_METHOD_NAME_LENGTH
|
||||
+ 1)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.update_notification_method, id,
|
||||
name=new_name_long, type=response_body['type'],
|
||||
address=response_body['address'], period=response_body['period'])
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_notification_method_invalid_type(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises(
|
||||
(exceptions.BadRequest, exceptions.NotFound, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.update_notification_method,
|
||||
id, name=response_body['name'], type='random',
|
||||
address=response_body['address'], period=0)
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_notification_method_address_exceeds_max_length(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
new_address_long = "x" * (
|
||||
constants.MAX_NOTIFICATION_METHOD_ADDRESS_LENGTH + 1)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.update_notification_method, id,
|
||||
name=response_body['name'], type=response_body['type'],
|
||||
address=new_address_long, period=response_body['period'])
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_notification_method_with_no_address(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises(
|
||||
(exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.update_notification_method_with_no_address, id,
|
||||
name="test_update_notification_method_name",
|
||||
type=response_body['type'])
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_create_and_delete_notification_method(self):
|
||||
notification = helpers.create_notification()
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = response_body['id']
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_delete_notification_method_with_invalid_id(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
id = data_utils.rand_name()
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.monasca_client.delete_notification_method,
|
||||
id)
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(response_body['id'])
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_patch_notification_method_with_invalid_id(self):
|
||||
id = data_utils.rand_name()
|
||||
name = data_utils.rand_name('notification-')
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.monasca_client.patch_notification_method,
|
||||
id, name)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_notification_method_with_invalid_id(self):
|
||||
id = data_utils.rand_name()
|
||||
name = data_utils.rand_name('notification-')
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.monasca_client.update_notification_method, id,
|
||||
name=name, type='EMAIL',
|
||||
address='bob@thebridge.org', period=0)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_notification_method_with_non_int_period(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.update_notification_method, id,
|
||||
name=response_body['name'], type=response_body['type'],
|
||||
address=response_body['name'], period='zero')
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_update_webhook_notification_method_with_invalid_period(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name,
|
||||
type='WEBHOOK',
|
||||
address='http://localhost/test01',
|
||||
period=60)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.update_notification_method, id,
|
||||
name=response_body['name'], type=response_body['type'],
|
||||
address=response_body['address'], period=5)
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_patch_notification_method_name(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(name, response_body['name'])
|
||||
id = response_body['id']
|
||||
new_name = name + 'update'
|
||||
resp, response_body = self.monasca_client.\
|
||||
patch_notification_method(id, new_name)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_name, response_body['name'])
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_patch_notification_method_type(self):
|
||||
type = 'EMAIL'
|
||||
notification = helpers.create_notification(type=type)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(type, response_body['type'])
|
||||
id = response_body['id']
|
||||
new_type = 'PAGERDUTY'
|
||||
resp, response_body = \
|
||||
self.monasca_client.\
|
||||
patch_notification_method(id, type=new_type)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_type, response_body['type'])
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_patch_notification_method_address(self):
|
||||
address = DEFAULT_EMAIL_ADDRESS
|
||||
notification = helpers.create_notification(address=address)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(address, response_body['address'])
|
||||
id = response_body['id']
|
||||
new_address = 'jane.doe@domain.com'
|
||||
resp, response_body = self.monasca_client.\
|
||||
patch_notification_method(id, address=new_address)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_address, response_body['address'])
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_patch_notification_method_address_period(self):
|
||||
type = 'WEBHOOK'
|
||||
notification = helpers.create_notification(
|
||||
type=type, address='http://localhost/test01', period=60)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertEqual(type, response_body['type'])
|
||||
id = response_body['id']
|
||||
|
||||
# test_patch_webhook_notification_to_email_with_zero_period
|
||||
new_type = 'EMAIL'
|
||||
new_period = 0
|
||||
resp, response_body = \
|
||||
self.monasca_client.\
|
||||
patch_notification_method(id, type=new_type,
|
||||
address='john.doe@domain.com',
|
||||
period=new_period)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_type, response_body['type'])
|
||||
self.assertEqual(new_period, response_body['period'])
|
||||
|
||||
# test_patch_email_notification_to_webhook_with_nonzero_period
|
||||
new_type = 'WEBHOOK'
|
||||
new_period = 60
|
||||
resp, response_body = \
|
||||
self.monasca_client.\
|
||||
patch_notification_method(id, type=new_type,
|
||||
address='http://localhost/test01',
|
||||
period=new_period)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(new_type, response_body['type'])
|
||||
self.assertEqual(new_period, response_body['period'])
|
||||
resp, response_body = self.monasca_client.\
|
||||
delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_patch_notification_method_name_exceeds_max_length(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
new_name_long = "x" * (constants.MAX_NOTIFICATION_METHOD_NAME_LENGTH
|
||||
+ 1)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.patch_notification_method, id,
|
||||
name=new_name_long)
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_patch_notification_method_invalid_type(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises(
|
||||
(exceptions.BadRequest, exceptions.NotFound, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.patch_notification_method, id, type='random')
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_patch_notification_method_address_exceeds_max_length(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
new_address_long = "x" * (
|
||||
constants.MAX_NOTIFICATION_METHOD_ADDRESS_LENGTH + 1)
|
||||
self.assertRaises(
|
||||
(exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.patch_notification_method, id, address=new_address_long)
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_patch_notification_method_with_non_int_period(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.patch_notification_method, id, period='zero')
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_patch_webhook_notification_method_with_invalid_period(self):
|
||||
name = data_utils.rand_name('notification-')
|
||||
notification = helpers.create_notification(name=name,
|
||||
type='WEBHOOK',
|
||||
address='http://localhost/test01',
|
||||
period=60)
|
||||
resp, response_body = self.monasca_client.create_notifications(
|
||||
notification)
|
||||
id = response_body['id']
|
||||
self.assertEqual(201, resp.status)
|
||||
self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity),
|
||||
self.monasca_client.patch_notification_method, id, period=5)
|
||||
resp, response_body = \
|
||||
self.monasca_client.delete_notification_method(id)
|
||||
self.assertEqual(204, resp.status)
|
||||
@@ -1,184 +0,0 @@
|
||||
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||
# 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 tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from monasca_tempest_tests.clients import api as clients
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
|
||||
|
||||
class TestReadOnlyRole(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestReadOnlyRole, cls).resource_setup()
|
||||
credentials = cls.cred_provider.get_creds_by_roles(
|
||||
['monasca-read-only-user']).credentials
|
||||
cls.os = clients.Manager(credentials=credentials)
|
||||
cls.monasca_client = cls.os.monasca_client
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestReadOnlyRole, cls).resource_cleanup()
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarms_success(self):
|
||||
resp, response_body = self.monasca_client.list_alarms()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any alarms)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarms'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_metrics_success(self):
|
||||
resp, response_body = self.monasca_client.list_metrics()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any metrics)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/metrics'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarm_definition_success(self):
|
||||
resp, response_body = self.monasca_client.list_alarm_definitions()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any alarm definitions)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarm-definitions'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_notification_methods_success(self):
|
||||
resp, response_body = self.monasca_client.list_notification_methods()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any notifications)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/notification-methods'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarm_count_success(self):
|
||||
resp, response_body = self.monasca_client.count_alarms()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any alarms to count)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, response_body['counts'][0][0])
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarms/count'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_alarm_state_history_success(self):
|
||||
resp, response_body = self.monasca_client.list_alarms_state_history()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any alarms that have history)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith('/v2.0/alarms/state-history'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_dimension_values_success(self):
|
||||
parms = '?dimension_name=foo'
|
||||
resp, response_body = self.monasca_client.list_dimension_values(parms)
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any metrics/dimensions)
|
||||
#
|
||||
url = '/v2.0/metrics/dimensions/names/values?dimension_name=foo'
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith(url))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_dimension_names_success(self):
|
||||
resp, response_body = self.monasca_client.list_dimension_names()
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any metrics/dimensions)
|
||||
#
|
||||
url = '/v2.0/metrics/dimensions/names'
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue(response_body['links'][0]['href'].endswith(url))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_measurements_success(self):
|
||||
start_timestamp = int(time.time() * 1000)
|
||||
start_time = str(helpers.timestamp_to_iso(start_timestamp))
|
||||
parms = '?name=foo&start_time=' + start_time
|
||||
resp, response_body = self.monasca_client.list_measurements(parms)
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any metrics to get measurements for)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue('/v2.0/metrics/measurements' in response_body['links'][0]['href'])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_success(self):
|
||||
start_timestamp = int(time.time() * 1000)
|
||||
start_time = str(helpers.timestamp_to_iso(start_timestamp))
|
||||
query_parms = '?name=foo&statistics=avg&start_time=' + start_time
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
#
|
||||
# Validate the call succeeds with empty result (we didn't
|
||||
# create any metrics to get statistics for)
|
||||
#
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(response_body['elements']))
|
||||
self.assertTrue('/v2.0/metrics/statistics' in response_body['links'][0]['href'])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_delete_alarms_fails(self):
|
||||
self.assertRaises(exceptions.Unauthorized,
|
||||
self.monasca_client.delete_alarm, "foo")
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_metric_fails(self):
|
||||
self.assertRaises(exceptions.Unauthorized,
|
||||
self.monasca_client.create_metrics,
|
||||
None)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_alarm_definition_fails(self):
|
||||
self.assertRaises(exceptions.Unauthorized,
|
||||
self.monasca_client.create_alarm_definitions,
|
||||
None)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_create_notification_fails(self):
|
||||
notif = helpers.create_notification()
|
||||
self.assertRaises(exceptions.Unauthorized,
|
||||
self.monasca_client.create_notifications,
|
||||
notif)
|
||||
@@ -1,514 +0,0 @@
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
|
||||
# (C) Copyright 2017-2018 SUSE LLC
|
||||
#
|
||||
# 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
|
||||
import urllib.parse as urlparse
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from monasca_tempest_tests.tests.api import constants
|
||||
from monasca_tempest_tests.tests.api import helpers
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
NUM_MEASUREMENTS = 100
|
||||
MIN_REQUIRED_MEASUREMENTS = 2
|
||||
WAIT_TIME = 30
|
||||
|
||||
|
||||
class TestStatistics(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestStatistics, cls).resource_setup()
|
||||
name = data_utils.rand_name('name')
|
||||
key = data_utils.rand_name('key')
|
||||
value1 = data_utils.rand_name('value1')
|
||||
value2 = data_utils.rand_name('value2')
|
||||
cls._test_name = name
|
||||
cls._test_key = key
|
||||
cls._test_value1 = value1
|
||||
cls._start_timestamp = int(time.time() * 1000)
|
||||
metrics = [
|
||||
helpers.create_metric(name=name,
|
||||
dimensions={key: value1},
|
||||
timestamp=cls._start_timestamp,
|
||||
value=1.23),
|
||||
helpers.create_metric(name=name,
|
||||
dimensions={key: value2},
|
||||
timestamp=cls._start_timestamp + 1000,
|
||||
value=4.56)
|
||||
]
|
||||
cls.metric_values = [m['value'] for m in metrics]
|
||||
cls.monasca_client.create_metrics(metrics)
|
||||
start_time_iso = helpers.timestamp_to_iso(cls._start_timestamp)
|
||||
query_param = '?name=' + str(name) + '&start_time=' + \
|
||||
start_time_iso + '&merge_metrics=true' + '&end_time=' + \
|
||||
helpers.timestamp_to_iso(cls._start_timestamp + 1000 * 2)
|
||||
start_time_iso = helpers.timestamp_to_iso(cls._start_timestamp)
|
||||
cls._start_time_iso = start_time_iso
|
||||
|
||||
num_measurements = 0
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = cls.monasca_client.\
|
||||
list_measurements(query_param)
|
||||
elements = response_body['elements']
|
||||
if len(elements) > 0:
|
||||
num_measurements = len(elements[0]['measurements'])
|
||||
if num_measurements >= MIN_REQUIRED_MEASUREMENTS:
|
||||
break
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
|
||||
if num_measurements < MIN_REQUIRED_MEASUREMENTS:
|
||||
assert False, "Required {} measurements, found {}".format(MIN_REQUIRED_MEASUREMENTS,
|
||||
num_measurements)
|
||||
|
||||
cls._end_timestamp = cls._start_timestamp + 3000
|
||||
cls._end_time_iso = helpers.timestamp_to_iso(cls._end_timestamp)
|
||||
|
||||
name2 = data_utils.rand_name("group-by")
|
||||
cls._group_by_metric_name = name2
|
||||
cls._group_by_end_time_iso = helpers.timestamp_to_iso(cls._start_timestamp + 4000)
|
||||
|
||||
group_by_metrics = [
|
||||
helpers.create_metric(name=name2,
|
||||
dimensions={'key1': 'value1', 'key2': 'value5', 'key3': 'value7'},
|
||||
timestamp=cls._start_timestamp + 1, value=2),
|
||||
helpers.create_metric(name=name2,
|
||||
dimensions={'key1': 'value2', 'key2': 'value5', 'key3': 'value7'},
|
||||
timestamp=cls._start_timestamp + 1001, value=3),
|
||||
helpers.create_metric(name=name2,
|
||||
dimensions={'key1': 'value3', 'key2': 'value6', 'key3': 'value7'},
|
||||
timestamp=cls._start_timestamp + 2001, value=5),
|
||||
helpers.create_metric(name=name2,
|
||||
dimensions={'key1': 'value4', 'key2': 'value6', 'key3': 'value8'},
|
||||
timestamp=cls._start_timestamp + 3001, value=7),
|
||||
]
|
||||
|
||||
cls.monasca_client.create_metrics(group_by_metrics)
|
||||
query_param = '?name=' + str(name2) + \
|
||||
'&start_time=' + start_time_iso + \
|
||||
'&merge_metrics=true' + \
|
||||
'&end_time=' + cls._group_by_end_time_iso
|
||||
|
||||
num_measurements = 0
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = cls.monasca_client. \
|
||||
list_measurements(query_param)
|
||||
elements = response_body['elements']
|
||||
if len(elements) > 0:
|
||||
num_measurements = len(elements[0]['measurements'])
|
||||
if num_measurements >= len(group_by_metrics):
|
||||
break
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
|
||||
if num_measurements < len(group_by_metrics):
|
||||
assert False, "Required {} measurements, found {}".format(len(group_by_metrics),
|
||||
response_body)
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TestStatistics, cls).resource_cleanup()
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics(self):
|
||||
self._test_list_statistic(with_end_time=True)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_no_end_time(self):
|
||||
self._test_list_statistic(with_end_time=False)
|
||||
|
||||
def _test_list_statistic(self, with_end_time=True):
|
||||
query_parms = '?name=' + str(self._test_name) + \
|
||||
'&statistics=' + urlparse.quote('avg,sum,min,max,count') + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&merge_metrics=true' + '&period=100000'
|
||||
if with_end_time is True:
|
||||
query_parms += '&end_time=' + str(self._end_time_iso)
|
||||
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
element = response_body['elements'][0]
|
||||
self._verify_element(element)
|
||||
column = element['columns']
|
||||
num_statistics_method = 5
|
||||
statistics = element['statistics'][0]
|
||||
self._verify_column_and_statistics(
|
||||
column, num_statistics_method, statistics, self.metric_values)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_no_name(self):
|
||||
query_parms = '?merge_metrics=true&statistics=avg&start_time=' + \
|
||||
str(self._start_time_iso) + '&end_time=' + \
|
||||
str(self._end_time_iso)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_statistics, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_no_statistics(self):
|
||||
query_parms = '?name=' + str(self._test_name) + '&start_time=' + str(
|
||||
self._start_time_iso) + '&end_time=' + str(self._end_time_iso)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_statistics, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_no_start_time(self):
|
||||
query_parms = '?name=' + str(self._test_name) + '&statistics=avg'
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_statistics, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_invalid_statistics(self):
|
||||
query_parms = '?name=' + str(self._test_name) + '&statistics=abc' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._end_time_iso)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_statistics, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_dimensions(self):
|
||||
query_parms = '?name=' + str(self._test_name) + '&statistics=avg' \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._end_time_iso) + \
|
||||
'&dimensions=' + str(self._test_key) + ':' + \
|
||||
str(self._test_value1) + '&period=100000'
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
dimensions = response_body['elements'][0]['dimensions']
|
||||
self.assertEqual(dimensions[self._test_key], self._test_value1)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_end_time_equals_start_time(self):
|
||||
query_parms = '?name=' + str(self._test_name) + \
|
||||
'&merge_metrics=true&statistics=avg&' \
|
||||
'start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._start_time_iso) + \
|
||||
'&period=100000'
|
||||
self.assertRaises(exceptions.BadRequest,
|
||||
self.monasca_client.list_statistics, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_period(self):
|
||||
query_parms = '?name=' + str(self._test_name) + \
|
||||
'&merge_metrics=true&statistics=avg&' \
|
||||
'start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._end_time_iso) + \
|
||||
'&period=1'
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
time_diff = self._end_timestamp - self._start_timestamp
|
||||
len_statistics = len(response_body['elements'][0]['statistics'])
|
||||
self.assertEqual(time_diff / 1000, len_statistics)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_offset_limit(self):
|
||||
start_timestamp = int(time.time() * 1000)
|
||||
name = data_utils.rand_name()
|
||||
metric = [
|
||||
helpers.create_metric(name=name, timestamp=start_timestamp + 1,
|
||||
dimensions={'key1': 'value-1',
|
||||
'key2': 'value-1'},
|
||||
value=1),
|
||||
helpers.create_metric(name=name, timestamp=start_timestamp + 1001,
|
||||
dimensions={'key1': 'value-2',
|
||||
'key2': 'value-2'},
|
||||
value=2),
|
||||
helpers.create_metric(name=name, timestamp=start_timestamp + 2001,
|
||||
dimensions={'key1': 'value-3',
|
||||
'key2': 'value-3'},
|
||||
value=3),
|
||||
helpers.create_metric(name=name, timestamp=start_timestamp + 3001,
|
||||
dimensions={'key1': 'value-4',
|
||||
'key2': 'value-4'},
|
||||
value=4)
|
||||
]
|
||||
|
||||
num_metrics = len(metric)
|
||||
self.monasca_client.create_metrics(metric)
|
||||
query_parms = '?name=' + name
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.list_metrics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
if elements and len(elements) == num_metrics:
|
||||
break
|
||||
else:
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
self._check_timeout(i, constants.MAX_RETRIES, elements, num_metrics)
|
||||
|
||||
start_time = helpers.timestamp_to_iso(start_timestamp)
|
||||
end_timestamp = start_timestamp + 4001
|
||||
end_time = helpers.timestamp_to_iso(end_timestamp)
|
||||
query_parms = '?name=' + name + '&merge_metrics=true&statistics=avg' \
|
||||
+ '&start_time=' + str(start_time) + '&end_time=' + \
|
||||
str(end_time) + '&period=1'
|
||||
resp, body = self.monasca_client.list_statistics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = body['elements'][0]['statistics']
|
||||
first_element = elements[0]
|
||||
|
||||
query_parms = '?name=' + name + '&merge_metrics=true&statistics=avg'\
|
||||
+ '&start_time=' + str(start_time) + '&end_time=' + \
|
||||
str(end_time) + '&period=1' + '&limit=' + str(num_metrics)
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements'][0]['statistics']
|
||||
self.assertEqual(num_metrics, len(elements))
|
||||
self.assertEqual(first_element, elements[0])
|
||||
|
||||
for limit in range(1, num_metrics):
|
||||
start_index = 0
|
||||
params = [('name', name),
|
||||
('merge_metrics', 'true'),
|
||||
('statistics', 'avg'),
|
||||
('start_time', str(start_time)),
|
||||
('end_time', str(end_time)),
|
||||
('period', 1),
|
||||
('limit', limit)]
|
||||
offset = None
|
||||
while True:
|
||||
num_expected_elements = limit
|
||||
if (num_expected_elements + start_index) > num_metrics:
|
||||
num_expected_elements = num_metrics - start_index
|
||||
|
||||
these_params = list(params)
|
||||
# If not the first call, use the offset returned by the last call
|
||||
if offset:
|
||||
these_params.extend([('offset', str(offset))])
|
||||
query_parms = '?' + urlencode(these_params)
|
||||
resp, response_body = self.monasca_client.list_statistics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
if not response_body['elements']:
|
||||
self.fail("No metrics returned")
|
||||
if not response_body['elements'][0]['statistics']:
|
||||
self.fail("No statistics returned")
|
||||
new_elements = response_body['elements'][0]['statistics']
|
||||
|
||||
self.assertEqual(num_expected_elements, len(new_elements))
|
||||
expected_elements = elements[start_index:start_index + limit]
|
||||
self.assertEqual(expected_elements, new_elements)
|
||||
start_index += num_expected_elements
|
||||
if start_index >= num_metrics:
|
||||
break
|
||||
# Get the next set
|
||||
offset = self._get_offset(response_body)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_group_by_one(self):
|
||||
query_parms = '?name=' + self._group_by_metric_name + \
|
||||
'&group_by=key2' + \
|
||||
'&statistics=max,avg,min' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._group_by_end_time_iso)
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 2)
|
||||
for statistics in elements:
|
||||
self.assertEqual(1, len(statistics['dimensions'].keys()))
|
||||
self.assertEqual([u'key2'], list(statistics['dimensions'].keys()))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_group_by_multiple(self):
|
||||
query_parms = '?name=' + self._group_by_metric_name + \
|
||||
'&group_by=key2,key3' + \
|
||||
'&statistics=max,avg,min' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._group_by_end_time_iso)
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 3)
|
||||
for statistics in elements:
|
||||
self.assertEqual(2, len(statistics['dimensions'].keys()))
|
||||
self.assertEqual({u'key2', u'key3'}, set(statistics['dimensions'].keys()))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_group_by_all(self):
|
||||
query_parms = '?name=' + self._group_by_metric_name + \
|
||||
'&group_by=*' + \
|
||||
'&statistics=max,avg,min' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._group_by_end_time_iso)
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
elements = response_body['elements']
|
||||
self.assertEqual(len(elements), 4)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_group_by_offset_limit(self):
|
||||
query_parms = '?name=' + str(self._group_by_metric_name) + \
|
||||
'&group_by=key2' + \
|
||||
'&statistics=avg,max' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._group_by_end_time_iso) + \
|
||||
'&period=1'
|
||||
resp, response_body = self.monasca_client.list_statistics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
all_expected_elements = response_body['elements']
|
||||
|
||||
for limit in range(1, 4):
|
||||
offset = None
|
||||
for i in range(4 - limit):
|
||||
query_parms = '?name=' + str(self._group_by_metric_name) + \
|
||||
'&group_by=key2' + \
|
||||
'&statistics=avg,max' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._group_by_end_time_iso) + \
|
||||
'&period=1' + \
|
||||
'&limit=' + str(limit)
|
||||
if i > 0:
|
||||
offset = self._get_offset(response_body)
|
||||
query_parms += "&offset=" + offset
|
||||
|
||||
expected_elements = helpers.get_expected_elements_inner_offset_limit(
|
||||
all_expected_elements,
|
||||
offset,
|
||||
limit,
|
||||
'statistics')
|
||||
|
||||
resp, response_body = self.monasca_client.list_statistics(query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(expected_elements, response_body['elements'])
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_with_long_start_time(self):
|
||||
query_parms = '?name=' + str(self._test_name) + \
|
||||
'&statistics=' + urlparse.quote('avg,sum,min,max,count') + \
|
||||
'&start_time=' + "2017-01-01T00:00:00.00Z" + \
|
||||
'&end_time=' + str(self._end_time_iso) + \
|
||||
'&merge_metrics=true' + '&period=100000'
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertTrue(set(['links', 'elements']) == set(response_body))
|
||||
element = response_body['elements'][0]
|
||||
self._verify_element(element)
|
||||
column = element['columns']
|
||||
num_statistics_method = 5
|
||||
statistics = element['statistics'][0]
|
||||
self._verify_column_and_statistics(
|
||||
column, num_statistics_method, statistics, self.metric_values)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_no_merge_metrics(self):
|
||||
key = data_utils.rand_name('key')
|
||||
value = data_utils.rand_name('value')
|
||||
metric3 = helpers.create_metric(
|
||||
name=self._test_name,
|
||||
dimensions={key: value},
|
||||
timestamp=self._start_timestamp + 2000)
|
||||
self.monasca_client.create_metrics(metric3)
|
||||
query_param = '?name=' + str(self._test_name) + '&start_time=' + \
|
||||
self._start_time_iso + '&end_time=' + helpers.\
|
||||
timestamp_to_iso(self._start_timestamp + 1000 * 4) + \
|
||||
'&merge_metrics=True'
|
||||
|
||||
for i in range(constants.MAX_RETRIES):
|
||||
resp, response_body = self.monasca_client.\
|
||||
list_measurements(query_param)
|
||||
elements = response_body['elements']
|
||||
for element in elements:
|
||||
if str(element['name']) == self._test_name and len(
|
||||
element['measurements']) == 3:
|
||||
end_time_iso = helpers.timestamp_to_iso(
|
||||
self._start_timestamp + 1000 * 4)
|
||||
query_parms = '?name=' + str(self._test_name) + \
|
||||
'&statistics=avg' + '&start_time=' + \
|
||||
str(self._start_time_iso) + '&end_time=' +\
|
||||
str(end_time_iso) + '&period=100000'
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.monasca_client.list_statistics,
|
||||
query_parms)
|
||||
return
|
||||
time.sleep(constants.RETRY_WAIT_SECS)
|
||||
self._check_timeout(i, constants.MAX_RETRIES, elements, 3)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
@decorators.attr(type=['negative'])
|
||||
def test_list_statistics_with_name_exceeds_max_length(self):
|
||||
long_name = "x" * (constants.MAX_LIST_STATISTICS_NAME_LENGTH + 1)
|
||||
query_parms = '?name=' + str(long_name) + '&merge_metrics=true' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._end_time_iso)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.list_statistics, query_parms)
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_list_statistics_response_body_statistic_result_type(self):
|
||||
query_parms = '?name=' + str(self._test_name) + '&period=100000' + \
|
||||
'&statistics=avg' + '&merge_metrics=true' + \
|
||||
'&start_time=' + str(self._start_time_iso) + \
|
||||
'&end_time=' + str(self._end_time_iso)
|
||||
resp, response_body = self.monasca_client.list_statistics(
|
||||
query_parms)
|
||||
self.assertEqual(200, resp.status)
|
||||
element = response_body['elements'][0]
|
||||
statistic = element['statistics']
|
||||
statistic_result_type = type(statistic[0][1])
|
||||
self.assertEqual(statistic_result_type, float)
|
||||
|
||||
def _verify_element(self, element):
|
||||
self.assertTrue(set(['id', 'name', 'dimensions', 'columns',
|
||||
'statistics']) == set(element))
|
||||
self.assertTrue(type(element['id']) is str)
|
||||
self.assertTrue(element['id'] is not None)
|
||||
self.assertTrue(type(element['name']) is str)
|
||||
self.assertTrue(type(element['dimensions']) is dict)
|
||||
self.assertEqual(len(element['dimensions']), 0)
|
||||
self.assertTrue(type(element['columns']) is list)
|
||||
self.assertTrue(type(element['statistics']) is list)
|
||||
self.assertEqual(element['name'], self._test_name)
|
||||
|
||||
def _verify_column_and_statistics(
|
||||
self, column, num_statistics_method, statistics, values):
|
||||
self.assertTrue(type(column) is list)
|
||||
self.assertTrue(type(statistics) is list)
|
||||
self.assertEqual(len(column), num_statistics_method + 1)
|
||||
self.assertEqual(column[0], 'timestamp')
|
||||
for i, method in enumerate(column):
|
||||
if method == 'avg':
|
||||
self.assertAlmostEqual(statistics[i], float(sum(values) / len(values)))
|
||||
elif method == 'max':
|
||||
self.assertEqual(statistics[i], max(values))
|
||||
elif method == 'min':
|
||||
self.assertEqual(statistics[i], min(values))
|
||||
elif method == 'sum':
|
||||
self.assertAlmostEqual(statistics[i], sum(values))
|
||||
elif method == 'count':
|
||||
self.assertEqual(statistics[i], len(values))
|
||||
|
||||
def _check_timeout(self, timer, max_retries, elements,
|
||||
expect_num_elements):
|
||||
if timer == max_retries - 1:
|
||||
error_msg = ("Failed: timeout on waiting for metrics: {} elements "
|
||||
"are needed. Current number of elements = {}").\
|
||||
format(expect_num_elements, len(elements))
|
||||
raise self.fail(error_msg)
|
||||
@@ -1,50 +0,0 @@
|
||||
# (C) Copyright 2015,2017 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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 datetime
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from monasca_tempest_tests.tests.api import base
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class TestVersions(base.BaseMonascaTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TestVersions, cls).resource_setup()
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_get_version(self):
|
||||
resp, response_body = self.monasca_client.get_version()
|
||||
self.assertEqual(resp.status, 200)
|
||||
response_body = json.loads(response_body)
|
||||
|
||||
self.assertIsInstance(response_body, dict)
|
||||
version = response_body
|
||||
self.assertTrue(set(['id', 'links', 'status', 'updated']) ==
|
||||
set(version))
|
||||
self.assertEqual(version['id'], u'v2.0')
|
||||
self.assertEqual(version['status'], u'CURRENT')
|
||||
date_object = datetime.datetime.strptime(version['updated'],
|
||||
"%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
self.assertIsInstance(date_object, datetime.datetime)
|
||||
links = response_body['links']
|
||||
self.assertIsInstance(links, list)
|
||||
link = links[0]
|
||||
self.assertTrue(set(['rel', 'href']) ==
|
||||
set(link))
|
||||
self.assertEqual(link['rel'], u'self')
|
||||
self.assertTrue(link['href'].endswith('/v2.0'))
|
||||
@@ -1,94 +0,0 @@
|
||||
# Copyright 2019 FUJITSU LIMITED
|
||||
#
|
||||
# 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 datetime import datetime
|
||||
import json
|
||||
from os import path
|
||||
import pytz
|
||||
import random
|
||||
import string
|
||||
|
||||
from oslo_config import cfg
|
||||
from tempest.common import credentials_factory as cred_factory
|
||||
from tempest import test
|
||||
|
||||
from monasca_tempest_tests.clients import event_api as clients
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def get_simple_event():
|
||||
json_file_path = path.join(path.dirname(__file__),
|
||||
'req_simple_event.json')
|
||||
with open(json_file_path, 'r') as f:
|
||||
return json.loads(f.read())
|
||||
|
||||
|
||||
def create_header(header={}, content_type='application/json'):
|
||||
header.update({'Content-Type': content_type,
|
||||
'kbn-version': CONF.monitoring.kibana_version})
|
||||
return header
|
||||
|
||||
|
||||
def generate_simple_event_type_string():
|
||||
letters = string.ascii_lowercase
|
||||
random_string = ''.join((random.choice(letters) for _ in range(15)))
|
||||
return '{}.{}'.format(random_string[:7], random_string[8:])
|
||||
|
||||
|
||||
def generate_simple_event(event_type=None, timestamp=None, events=None, num_of_events=1):
|
||||
if event_type is None:
|
||||
event_type = generate_simple_event_type_string()
|
||||
if events is None:
|
||||
events = \
|
||||
[{
|
||||
'dimensions': {
|
||||
'service': 'compute',
|
||||
'topic': 'notification.sample',
|
||||
'hostname': 'mars'},
|
||||
'project_id': '6f70656e737461636b20342065766572',
|
||||
'event': {
|
||||
'event_type': event_type,
|
||||
'payload': {
|
||||
'nova_object.data': {
|
||||
'architecture': 'x86_64',
|
||||
'availability_zone': 'nova',
|
||||
'created_at': '2012-10-29T13:42:11Z'}}}}] \
|
||||
* num_of_events
|
||||
if timestamp is None:
|
||||
timestamp = datetime.now(tz=pytz.utc).strftime("%Y-%m-%dT%H:%M:%SZ%z")
|
||||
return {'timestamp': timestamp,
|
||||
'events': events}
|
||||
|
||||
|
||||
class BaseEventsTestCase(test.BaseTestCase):
|
||||
"""Base test case class for all Event API tests."""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseEventsTestCase, cls).skip_checks()
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseEventsTestCase, cls).resource_setup()
|
||||
auth_version = CONF.identity.auth_version
|
||||
cred_provider = cred_factory.get_credentials_provider(
|
||||
cls.__name__,
|
||||
identity_version=auth_version)
|
||||
credentials = cred_provider.get_creds_by_roles(
|
||||
['admin']).credentials
|
||||
cls.os_primary = clients.Manager(credentials=credentials)
|
||||
|
||||
cls.events_api_client = cls.os_primary.events_api_client
|
||||
cls.events_search_client = cls.os_primary.events_search_client
|
||||
@@ -1,76 +0,0 @@
|
||||
# Copyright 2019 FUJITSU LIMITED
|
||||
#
|
||||
# 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.common.utils import test_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from monasca_tempest_tests.tests.event_api import base
|
||||
|
||||
RETRY_COUNT = 15
|
||||
RETRY_WAIT = 2
|
||||
|
||||
|
||||
class BaseEventsTestCase(base.BaseEventsTestCase):
|
||||
|
||||
def check_if_events_are_present_in_db(self, event_type):
|
||||
return len(self.events_search_client.search_event_by_event_type(event_type)) > 0
|
||||
|
||||
def send_and_retrieve_events(self, events=None, header=None, events_number=1):
|
||||
if events is None:
|
||||
event_type = base.generate_simple_event_type_string()
|
||||
events = base.generate_simple_event(event_type, num_of_events=events_number)
|
||||
if header is None:
|
||||
header = base.create_header()
|
||||
response, _ = self.events_api_client.send_events(events, header)
|
||||
self.assertEqual(200, response.status)
|
||||
test_utils.call_until_true(self.check_if_events_are_present_in_db,
|
||||
RETRY_COUNT * RETRY_WAIT,
|
||||
RETRY_WAIT,
|
||||
event_type)
|
||||
response = self.events_search_client.search_event_by_event_type(event_type)
|
||||
|
||||
self.assertEqual(events_number, len(response))
|
||||
|
||||
@decorators.attr(type=['gate', 'smoke'])
|
||||
def test_single_event(self):
|
||||
self.send_and_retrieve_events()
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_multiple_events(self):
|
||||
self.send_and_retrieve_events(events_number=5)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_missing_body(self):
|
||||
header = base.create_header()
|
||||
try:
|
||||
response, _ = self.events_api_client.send_events(None, header)
|
||||
except exceptions.UnprocessableEntity as exc:
|
||||
self.assertEqual(422, exc.resp.status)
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_empty_event(self):
|
||||
header = base.create_header()
|
||||
body = base.generate_simple_event(events=[])
|
||||
try:
|
||||
response, _ = self.events_api_client.send_events(body, header)
|
||||
except exceptions.UnprocessableEntity as exc:
|
||||
self.assertEqual(422, exc.resp.status)
|
||||
|
||||
def test_empty_content_type(self):
|
||||
body = base.generate_simple_event()
|
||||
try:
|
||||
response, _ = self.events_api_client.send_events(body, {})
|
||||
except exceptions.BadRequest as exc:
|
||||
self.assertEqual(400, exc.resp.status)
|
||||
@@ -1,138 +0,0 @@
|
||||
# coding=utf-8
|
||||
#
|
||||
# Copyright 2015 FUJITSU LIMITED
|
||||
#
|
||||
# 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 random
|
||||
import string
|
||||
|
||||
from oslo_config import cfg
|
||||
from tempest.common import credentials_factory as cred_factory
|
||||
from tempest import exceptions
|
||||
from tempest import test
|
||||
|
||||
from monasca_tempest_tests.clients import log_api as clients
|
||||
|
||||
CONF = cfg.CONF
|
||||
_ONE_MB = 1024 * 1024 # MB
|
||||
|
||||
|
||||
def _get_message_size(size_base):
|
||||
"""Returns message size in number of characters.
|
||||
|
||||
Method relies on UTF-8 where 1 character = 1 byte.
|
||||
|
||||
"""
|
||||
return int(round(size_base * _ONE_MB, 1))
|
||||
|
||||
|
||||
_SMALL_MESSAGE_SIZE = _get_message_size(0.001)
|
||||
_MEDIUM_MESSAGE_SIZE = _get_message_size(0.01)
|
||||
_LARGE_MESSAGE_SIZE = _get_message_size(0.1)
|
||||
# rejectable message must be larger than [service]max_log_size
|
||||
# from monasca-log-api.conf
|
||||
_reject_size = CONF.monitoring.log_api_max_log_size/_ONE_MB + 0.1
|
||||
_REJECTABLE_MESSAGE_SIZE = _get_message_size(_reject_size)
|
||||
|
||||
|
||||
def generate_unique_message(message=None, size=50):
|
||||
letters = string.ascii_lowercase
|
||||
|
||||
def rand(amount, space=True):
|
||||
space = ' ' if space else ''
|
||||
return ''.join((random.choice(letters + space) for _ in range(amount)))
|
||||
|
||||
sid = rand(10, space=False)
|
||||
|
||||
if not message:
|
||||
message = rand(size)
|
||||
return sid, sid + ' ' + message
|
||||
|
||||
|
||||
def generate_small_message(message=None):
|
||||
return generate_unique_message(message, _SMALL_MESSAGE_SIZE)
|
||||
|
||||
|
||||
def generate_medium_message(message=None):
|
||||
return generate_unique_message(message, _MEDIUM_MESSAGE_SIZE)
|
||||
|
||||
|
||||
def generate_large_message(message=None):
|
||||
return generate_unique_message(message, _LARGE_MESSAGE_SIZE)
|
||||
|
||||
|
||||
def generate_rejectable_message(message=None):
|
||||
return generate_unique_message(message, _REJECTABLE_MESSAGE_SIZE)
|
||||
|
||||
|
||||
def _get_headers(headers=None, content_type="application/json"):
|
||||
if not headers:
|
||||
headers = {}
|
||||
headers.update({
|
||||
'Content-Type': content_type,
|
||||
'kbn-version': CONF.monitoring.kibana_version,
|
||||
'kbn-xsrf': 'kibana'
|
||||
})
|
||||
return headers
|
||||
|
||||
|
||||
def _get_data(message):
|
||||
data = {
|
||||
'logs': [{
|
||||
'message': message
|
||||
}]
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
class BaseLogsTestCase(test.BaseTestCase):
|
||||
"""Base test case class for all Logs tests."""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseLogsTestCase, cls).skip_checks()
|
||||
if not CONF.service_available.logs:
|
||||
raise cls.skipException("Monasca logs support is required")
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseLogsTestCase, cls).resource_setup()
|
||||
auth_version = CONF.identity.auth_version
|
||||
cred_provider = cred_factory.get_credentials_provider(
|
||||
cls.__name__,
|
||||
identity_version=auth_version)
|
||||
credentials = cred_provider.get_creds_by_roles(
|
||||
['monasca-user', 'admin']).credentials
|
||||
cls.os_primary = clients.Manager(credentials=credentials)
|
||||
|
||||
cls.logs_client = cls.os_primary.log_api_client
|
||||
cls.logs_search_client = cls.os_primary.log_search_client
|
||||
|
||||
@staticmethod
|
||||
def cleanup_resources(method, list_of_ids):
|
||||
for resource_id in list_of_ids:
|
||||
try:
|
||||
method(resource_id)
|
||||
except exceptions.EndpointNotFound:
|
||||
pass
|
||||
|
||||
|
||||
class BaseLogsSearchTestCase(BaseLogsTestCase):
|
||||
"""Base test case class for all LogsSearch tests."""
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseLogsSearchTestCase, cls).skip_checks()
|
||||
# logs-search tests need both, 'logs' and 'logs-search'
|
||||
if not CONF.service_available.logs_search:
|
||||
raise cls.skipException("Monasca logs-search support is required")
|
||||
@@ -1,103 +0,0 @@
|
||||
# Copyright 2015-2017 FUJITSU LIMITED
|
||||
#
|
||||
# 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 decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from monasca_tempest_tests.tests.log_api import base
|
||||
|
||||
|
||||
class TestLogApiConstraints(base.BaseLogsTestCase):
|
||||
@decorators.attr(type='gate')
|
||||
def test_should_reject_if_body_is_empty(self):
|
||||
headers = base._get_headers()
|
||||
try:
|
||||
self.logs_client.custom_request('POST', headers, None)
|
||||
except exceptions.BadRequest as urc:
|
||||
self.assertEqual(400, urc.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with an error')
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_should_reject_if_content_type_missing(self):
|
||||
headers = base._get_headers(content_type='')
|
||||
try:
|
||||
self.logs_client.custom_request('POST', headers, '{}')
|
||||
except exceptions.BadRequest as urc:
|
||||
self.assertEqual(400, urc.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with 400')
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_should_reject_if_wrong_content_type(self):
|
||||
headers = base._get_headers(content_type='video/3gpp')
|
||||
try:
|
||||
self.logs_client.custom_request('POST', headers, '{}')
|
||||
except exceptions.InvalidContentType as urc:
|
||||
self.assertEqual(415, urc.resp.status)
|
||||
return
|
||||
except exceptions.BadRequest as urc:
|
||||
self.assertEqual(400, urc.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with 400')
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_should_reject_too_big_message(self):
|
||||
_, message = base.generate_rejectable_message()
|
||||
headers = base._get_headers(self.logs_client.get_headers())
|
||||
# Add 'Connection: Keep-Alive' to send large message before
|
||||
# connection is closed by client. In class ClosingHttp is added
|
||||
# header 'connection:close' (which will cause closing socket before sending whole message).
|
||||
# Data are send in small TCP packages.
|
||||
# Without this header set to Keep-Alive Tempest lib will try to retry connection and finally
|
||||
# raise ProtocolError.
|
||||
headers.update({'Connection': 'Keep-Alive'})
|
||||
data = base._get_data(message)
|
||||
try:
|
||||
self.logs_client.send_single_log(data, headers)
|
||||
except exceptions.OverLimit as urc:
|
||||
self.assertEqual(413, urc.resp.status)
|
||||
return
|
||||
except exceptions.UnexpectedContentType as uct:
|
||||
self.assertEqual(503, uct.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with 413 or 503')
|
||||
|
||||
@decorators.attr(type='gate')
|
||||
def test_should_reject_too_big_message_multiline(self):
|
||||
_, message = base.generate_rejectable_message()
|
||||
message = message.replace(' ', '\n')
|
||||
headers = base._get_headers(self.logs_client.get_headers())
|
||||
# Add Connection: Keep-Alive to send large message before
|
||||
# connection is closed by cli. In class ClosingHttp is added
|
||||
# header connection:close (which will cause closing socket before sending whole message).
|
||||
# Data are send in small TCP packages.
|
||||
# Without this header set to Keep-Alive Tempest lib will try to retry connection and finally
|
||||
# raise ProtocolError.
|
||||
headers.update({'Connection': 'Keep-Alive'})
|
||||
data = base._get_data(message)
|
||||
try:
|
||||
self.logs_client.send_single_log(data, headers)
|
||||
except exceptions.OverLimit as urc:
|
||||
self.assertEqual(413, urc.resp.status)
|
||||
return
|
||||
except exceptions.UnexpectedContentType as uct:
|
||||
self.assertEqual(503, uct.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with 413 or 503')
|
||||
@@ -1,107 +0,0 @@
|
||||
# Copyright 2015-2017 FUJITSU LIMITED
|
||||
#
|
||||
# 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 import config
|
||||
from tempest.lib.common.utils import test_utils
|
||||
from tempest.lib import decorators
|
||||
from testtools import matchers
|
||||
|
||||
from monasca_tempest_tests.tests.log_api import base
|
||||
|
||||
CONF = config.CONF
|
||||
_RETRY_COUNT = 15
|
||||
_RETRY_WAIT = 2
|
||||
|
||||
|
||||
class TestSingleLog(base.BaseLogsSearchTestCase):
|
||||
def _run_and_wait(self, key, data,
|
||||
content_type='application/json',
|
||||
headers=None, fields=None):
|
||||
|
||||
headers = base._get_headers(headers, content_type)
|
||||
|
||||
def wait():
|
||||
return self.logs_search_client.count_search_messages(key,
|
||||
headers) > 0
|
||||
|
||||
self.assertEqual(0, self.logs_search_client.count_search_messages(key,
|
||||
headers),
|
||||
'Find log message in elasticsearch: {0}'.format(key))
|
||||
|
||||
headers = base._get_headers(headers, content_type)
|
||||
data = base._get_data(data)
|
||||
|
||||
client = self.logs_client
|
||||
response, _ = client.send_single_log(data, headers, fields)
|
||||
self.assertEqual(204, response.status)
|
||||
|
||||
test_utils.call_until_true(wait, _RETRY_COUNT * _RETRY_WAIT,
|
||||
_RETRY_WAIT)
|
||||
response = self.logs_search_client.search_messages(key, headers)
|
||||
self.assertEqual(1, len(response))
|
||||
|
||||
return response
|
||||
|
||||
@decorators.attr(type=["gate", "smoke"])
|
||||
def test_small_message(self):
|
||||
self._run_and_wait(*base.generate_small_message())
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_medium_message(self):
|
||||
self._run_and_wait(*base.generate_medium_message())
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_big_message(self):
|
||||
self._run_and_wait(*base.generate_large_message())
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_small_message_multiline(self):
|
||||
sid, message = base.generate_small_message()
|
||||
self._run_and_wait(sid, message.replace(' ', '\n'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_medium_message_multiline(self):
|
||||
sid, message = base.generate_medium_message()
|
||||
self._run_and_wait(sid, message.replace(' ', '\n'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_big_message_multiline(self):
|
||||
sid, message = base.generate_large_message()
|
||||
self._run_and_wait(sid, message.replace(' ', '\n'))
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_send_cross_project(self):
|
||||
sid, message = base.generate_small_message()
|
||||
headers = {'X-Roles': 'admin, monitoring-delegate'}
|
||||
cross_tennant_id = '2106b2c8da0eecdb3df4ea84a0b5624b'
|
||||
fields = {'project_id': cross_tennant_id}
|
||||
response = self._run_and_wait(sid, message, headers=headers, fields=fields)
|
||||
log_msg = response[0]
|
||||
for key in CONF.monitoring.log_project_id_path:
|
||||
log_msg = log_msg.pop(key)
|
||||
self.assertThat(log_msg,
|
||||
matchers.StartsWith(cross_tennant_id))
|
||||
|
||||
# TODO(trebski) following test not passing - failed to retrieve
|
||||
# big message from elasticsearch
|
||||
|
||||
# @decorators.attr(type='gate')
|
||||
# def test_should_truncate_big_message(self):
|
||||
# message_size = base._get_message_size(0.9999)
|
||||
# sid, message = base.generate_unique_message(size=message_size)
|
||||
#
|
||||
# headers = base._get_headers(self.logs_clients.get_headers())
|
||||
# response = self._run_and_wait(sid, message, headers=headers)
|
||||
#
|
||||
# self.assertTrue(False, 'API should respond with 500')
|
||||
@@ -1,61 +0,0 @@
|
||||
# Copyright 2017 FUJITSU LIMITED
|
||||
#
|
||||
# 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.common.utils import test_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from monasca_common.tests.validation import test_metric_validation
|
||||
from monasca_tempest_tests.tests.log_api import base
|
||||
|
||||
_RETRY_COUNT = 15
|
||||
_RETRY_WAIT = 2
|
||||
_UNICODE_CASES = test_metric_validation.UNICODE_MESSAGES
|
||||
|
||||
|
||||
class TestUnicodeV3(base.BaseLogsSearchTestCase):
|
||||
|
||||
def _run_and_wait(self, key, data,
|
||||
content_type='application/json',
|
||||
headers=None, fields=None):
|
||||
|
||||
headers = base._get_headers(headers, content_type)
|
||||
|
||||
def wait():
|
||||
return self.logs_search_client.count_search_messages(key,
|
||||
headers) > 0
|
||||
|
||||
self.assertEqual(0, self.logs_search_client.count_search_messages(key,
|
||||
headers),
|
||||
'Find log message in elasticsearch: {0}'.format(key))
|
||||
|
||||
headers = base._get_headers(headers, content_type)
|
||||
data = base._get_data(data)
|
||||
|
||||
client = self.logs_client
|
||||
response, _ = client.send_single_log(data, headers, fields)
|
||||
self.assertEqual(204, response.status)
|
||||
|
||||
test_utils.call_until_true(wait, _RETRY_COUNT * _RETRY_WAIT,
|
||||
_RETRY_WAIT)
|
||||
response = self.logs_search_client.search_messages(key, headers)
|
||||
self.assertEqual(1, len(response))
|
||||
|
||||
return response
|
||||
|
||||
@decorators.attr(type="gate")
|
||||
def test_unicode_message(self):
|
||||
for m in _UNICODE_CASES:
|
||||
self._run_and_wait(*base.generate_small_message(m['input']), headers={
|
||||
'LA-Unicode-Case': m['case']
|
||||
})
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
- hosts: all
|
||||
tasks:
|
||||
- name: Login to Dockerhub
|
||||
command: "docker login -u {{ doker_hub_login_tempest.user }} -p {{ doker_hub_login_tempest.password }}"
|
||||
no_log: true
|
||||
|
||||
- name: List images
|
||||
shell: "docker images --format '{% raw %}{{ .Repository }}:{{ .Tag }}{% endraw %}' | grep monasca"
|
||||
|
||||
- name: Push to Docker Hub all tags
|
||||
shell: "docker push monasca/tempest-tests:{{ zuul.tag if zuul.pipeline == 'release' else zuul.branch }}"
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of monasca-tempest-plugin
|
||||
to support py2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by monasca-tempest-plugin is Python 3.5.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- |
|
||||
Update alarms count tests to test a query string with multiple
|
||||
metric_dimension values.
|
||||
@@ -1,268 +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.
|
||||
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
copyright = u'2017, OpenStack Developers'
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/openstack'
|
||||
openstackdocs_bug_project = 'monasca_tempest_plugin'
|
||||
openstackdocs_bug_tag = ''
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = ''
|
||||
# The short X.Y version.
|
||||
version = ''
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
|
||||
# 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
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'monasca_tempest_pluginReleaseNotesdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'monasca_tempest_pluginReleaseNotes.tex',
|
||||
u'monasca_tempest_plugin Release Notes Documentation',
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'monasca_tempest_pluginrereleasenotes',
|
||||
u'monasca_tempest_plugin Release Notes Documentation',
|
||||
[u'OpenStack Foundation'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'monasca_tempest_plugin ReleaseNotes',
|
||||
u'monasca_tempest_plugin Release Notes Documentation',
|
||||
u'OpenStack Foundation', 'monasca_tempest_pluginReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
||||
@@ -1,8 +0,0 @@
|
||||
============================================
|
||||
monasca_tempest_plugin Release Notes
|
||||
============================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
unreleased
|
||||
@@ -1,5 +0,0 @@
|
||||
==============================
|
||||
Current Series Release Notes
|
||||
==============================
|
||||
|
||||
.. release-notes::
|
||||
@@ -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.
|
||||
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
oslo.config>=5.2.0 # Apache-2.0
|
||||
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
tempest>=17.1.0 # Apache-2.0
|
||||
oslotest>=3.2.0 # Apache-2.0
|
||||
mock>=2.0.0 # BSD
|
||||
monasca-common>=2.8.0 # Apache-2.0
|
||||
pytz>=2013.6 # MIT
|
||||
29
setup.cfg
29
setup.cfg
@@ -1,29 +0,0 @@
|
||||
[metadata]
|
||||
name = monasca-tempest-plugin
|
||||
summary = Tempest plugin for Monasca project
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-discuss@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/monasca-api/latest/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Topic :: System :: Monitoring
|
||||
|
||||
[files]
|
||||
packages =
|
||||
monasca_tempest_tests
|
||||
|
||||
[entry_points]
|
||||
tempest.test_plugins =
|
||||
monasca_tests = monasca_tempest_tests.plugin:MonascaTempestPlugin
|
||||
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,5 +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;python_version>='3.5' # Apache-2.0
|
||||
58
tox.ini
58
tox.ini
@@ -1,58 +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
|
||||
OS_STDOUT_CAPTURE=1
|
||||
OS_STDERR_CAPTURE=1
|
||||
OS_TEST_TIMEOUT=60
|
||||
deps =
|
||||
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHON=coverage run --source monasca_tempest_plugin --parallel-mode
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
|
||||
[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:releasenotes]
|
||||
deps = {[testenv:docs]deps}
|
||||
commands =
|
||||
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[flake8]
|
||||
# H106: Don't put vim configuration in source files
|
||||
# H203: Use assertIs(Not)None to check for None
|
||||
enable-extensions=H106,H203
|
||||
max-complexity = 50
|
||||
max-line-length = 100
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools,build
|
||||
show-source = True
|
||||
Reference in New Issue
Block a user