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 @@
|
|||||||
======================
|
This project is no longer maintained.
|
||||||
Monasca Tempest Plugin
|
|
||||||
======================
|
|
||||||
|
|
||||||
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.
|
For any further questions, please email openstack-discuss@lists.openstack.org
|
||||||
|
or join #openstack-dev on OFTC.
|
||||||
* 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
|
|
||||||
|
|||||||
@@ -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