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:
Goutham Pacha Ravi
2025-08-11 22:15:30 -07:00
parent fc26314fa8
commit 4bf94f4cb0
79 changed files with 7 additions and 9635 deletions

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
[DEFAULT]
test_path=./monasca_tempest_plugin/tests
top_dir=./

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,9 @@
======================
Monasca Tempest Plugin
======================
This project is no longer maintained.
Tempest plugin for Monasca API and Log API Project
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
It contains the tempest plugin for the functional testing of Monasca Project.
* Free software: Apache license
* Documentation: https://docs.openstack.org/monasca-tempest-plugin/latest/
* Release notes: https://docs.openstack.org/releasenotes/monasca-api/
* Source: https://opendev.org/openstack/monasca-tempest-plugin
* Bugs: https://storyboard.openstack.org/#!/project/919
For any further questions, please email openstack-discuss@lists.openstack.org
or join #openstack-dev on OFTC.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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'')
key = data_utils.rand_name(u'')
value = data_utils.rand_name(u'')
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_')
value_meta_value = data_utils.rand_name(u'value_meta_')
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +0,0 @@
---
other:
- |
Update alarms count tests to test a query string with multiple
metric_dimension values.

View File

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

View File

@@ -1,8 +0,0 @@
============================================
monasca_tempest_plugin Release Notes
============================================
.. toctree::
:maxdepth: 1
unreleased

View File

@@ -1,5 +0,0 @@
==============================
Current Series Release Notes
==============================
.. release-notes::

View File

@@ -1,13 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
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

View File

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

View File

@@ -1,29 +0,0 @@
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=2.0.0'],
pbr=True)

View File

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

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