Update monitorstack to use updated openstacksdk and es

This change updates the functions so that they work with the most up to date
OpenStack SDK, and adds an elasticsearch output format.

Tests have been udpated to pass newer versions of flake8 and py3

Zuul project config has been added

Change-Id: If46d3fb8e2b2e4aa5e21bf7da470945b05d216cf
Signed-off-by: cloudnull <kevin@cloudnull.com>
This commit is contained in:
Kevin Carter 2019-02-15 12:22:23 -06:00 committed by cloudnull
parent b676952bbe
commit 034228e754
No known key found for this signature in database
GPG Key ID: 9443251A787B9FB3
26 changed files with 507 additions and 151 deletions

52
bindep.txt Normal file
View File

@ -0,0 +1,52 @@
# This file facilitates OpenStack-CI package installation
# before the execution of any tests.
#
# See the following for details:
# - https://docs.openstack.org/infra/bindep/
# - https://git.openstack.org/cgit/openstack-infra/bindep
#
# Even if the role does not make use of this facility, it
# is better to have this file empty, otherwise OpenStack-CI
# will fall back to installing its default packages which
# will potentially be detrimental to the tests executed.
#
# Note:
# This file is maintained in the openstack-ansible-tests repository.
# https://git.openstack.org/cgit/openstack/openstack-ansible-tests/tree/bindep.txt
# If you need to remove or add extra dependencies, you should modify
# the central file instead and once your change is accepted then update
# this file as well. The purpose of this file is to ensure that Python and
# Ansible have all their necessary binary requirements on the test host before
# tox executes. Any binary requirements needed by services/roles should be
# installed by those roles in their applicable package install tasks, not through
# using this file.
#
# The gcc compiler
gcc
# Base requirements for Ubuntu
git-core [platform:dpkg]
libssl-dev [platform:dpkg]
libffi-dev [platform:dpkg]
python2.7 [platform:dpkg]
python-apt [platform:dpkg]
python-dev [platform:dpkg]
python3 [platform:dpkg]
python3-apt [platform:dpkg]
python3-dev [platform:dpkg]
# Base requirements for RPM distros
gcc-c++ [platform:rpm]
git [platform:rpm]
libffi-devel [platform:rpm]
openssl-devel [platform:rpm]
python-devel [platform:rpm]
python2-dnf [platform:fedora]
# For SELinux
libselinux-python [platform:redhat]
libsemanage-python [platform:redhat]
# Required for compressing collected log files in CI
gzip

View File

@ -14,6 +14,13 @@ auth_url = https://127.0.0.1:5000/v3
username = admin
password = Secrete
# NOTE(Cloudnull):
# If the system already has a clouds.yaml configuration file in place, monitorstack
# can use this config by default. Create the "cloud" section and set the cloud option.
# When using this section, no other OpenStack options are needed.
# [cloud]
# cloud = default
[keystone]
# NOTE(cloudnull):
# When using keystone V3 you will need the .*domain_name configuration options.
@ -45,3 +52,13 @@ project_name = ironic
user_domain_name = users
project_domain_name = projects
password = SuperSecrete
[elasticsearch]
# List of hosts. Note, items in this list are strings with the hostname or IP only.
hosts = ['localhost']
# Optional settings when using authenticated environments.
# http_auth = ('user', 'secret')
# Optional settings when using SSL.
# scheme="https"
# port=443

View File

@ -83,7 +83,8 @@ VALID_OUTPUT_FORMATS = [
'json',
'line',
'telegraf',
'rax-maas'
'rax-maas',
'elasticsearch'
]
@ -96,6 +97,9 @@ VALID_OUTPUT_FORMATS = [
', '.join(VALID_OUTPUT_FORMATS)
),
)
@click.option('--config-file',
help='MonitorStack configuration file',
default='openstack.ini')
@click.option('-v', '--verbose', is_flag=True, help='Enables verbose mode.')
@pass_context
def cli(*args, **kwargs):
@ -122,7 +126,7 @@ def process_result(results, output_format, **kwargs):
exit_code = 0
for result in results:
output_formatter(result)
output_formatter(result, kwargs['config_file'])
if result['exit_code'] != 0:
exit_code = result['exit_code']
else:

View File

@ -18,15 +18,17 @@ import time
import click
from monitorstack import utils
def write_json(result):
def write_json(result, config_file):
"""Output in raw JSON format."""
output = json.dumps(result, indent=2)
click.echo(output)
return True
def write_line(result):
def write_line(result, config_file):
"""Output in line format."""
for key, value in result['variables'].items():
click.echo("{} {}".format(key, value))
@ -102,7 +104,7 @@ def _telegraf_line_format(sets, quote=False):
return ','.join(store).rstrip(',')
def write_telegraf(result):
def write_telegraf(result, config_file):
"""Output in telegraf format."""
resultant = [result['measurement_name']]
if 'meta' in result:
@ -120,7 +122,7 @@ def write_telegraf(result):
return True
def write_rax_maas(result):
def write_rax_maas(result, config_file):
"""Output in Rackspace Monitoring as a Service format."""
status = ['status']
if result['exit_code'] == 0:
@ -142,3 +144,37 @@ def write_rax_maas(result):
click.echo(' '.join(metric))
return True
def write_elasticsearch(result, config_file):
"""Output in elasticsearch format."""
import datetime
from elasticsearch import Elasticsearch
config = utils.read_config(config_file=config_file)
if 'elasticsearch' in config:
elastcisearch_config = config['elasticsearch']
es = Elasticsearch(**elastcisearch_config)
else:
es = Elasticsearch()
doc = {
'author': 'openstack',
'text': result['message'],
'timestamp': datetime.datetime.now(),
'measurement_name': result['measurement_name'],
'meta': result['meta'],
'variables': result['variables'],
'status': result['exit_code']
}
res = es.index(
index="monitorstack-{}".format(
datetime.date.today().strftime("%Y-%m-%d")
),
id=_current_time(),
doc_type='openstack-metrics',
body=doc
)
click.echo(res['result'])

View File

@ -25,7 +25,7 @@ COMMAND_NAME = 'os_block_pools_totals'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -43,14 +43,22 @@ def cli(ctx, config_file):
},
'variables': {}
}
config = utils.read_config(config_file=config_file)['cinder']
interface = config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=config)
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('cinder')
cloud_config = os_config.get('cloud')
if service_config:
_ost = ost.OpenStack(os_auth_args=service_config)
else:
_ost = ost.OpenStack(os_auth_args=cloud_config)
try:
variables = output['variables']
total_capacity_gb = 0
free_capacity_gb = 0
for item in _ost.get_volume_pool_stats(interface=interface):
for item in _ost.get_volume_pool_stats():
cap = item['capabilities']
output['meta'][cap.get('pool_name')] = True
free_capacity_gb += float(cap.get('free_capacity_gb', 0))

View File

@ -25,7 +25,7 @@ COMMAND_NAME = 'os_block_pools_usage'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -43,28 +43,37 @@ def cli(ctx, config_file):
},
'variables': {}
}
config = utils.read_config(config_file=config_file)['cinder']
interface = config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=config)
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('cinder')
cloud_config = os_config.get('cloud')
if service_config:
_ost = ost.OpenStack(os_auth_args=service_config)
else:
_ost = ost.OpenStack(os_auth_args=cloud_config)
try:
variables = output['variables']
for item in _ost.get_volume_pool_stats(interface=interface):
cap = item['capabilities']
total_capacity_gb = float(cap.get('total_capacity_gb', 0))
free_capacity_gb = float(cap.get('free_capacity_gb', 0))
percent_used = 100 * (free_capacity_gb / total_capacity_gb)
pool_name = cap.get('pool_name')
for item in _ost.get_volume_pool_stats():
cap = item.capabilities
pool_name = cap.get('volume_backend_name') or item.name
if pool_name in output['meta']:
continue
else:
output['meta'][pool_name] = True
total_capacity_gb = float(cap.get('total_capacity_gb', 0))
free_capacity_gb = float(cap.get('free_capacity_gb', 0))
percent_used = 100 * (free_capacity_gb / total_capacity_gb)
free_metric = '{}_free_capacity_gb'.format(pool_name)
variables[free_metric] = free_capacity_gb
output['meta'][pool_name] = True
total_metric = '{}_total_capacity_gb'.format(pool_name)
variables[total_metric] = total_capacity_gb
free_metric = '{}_free_capacity_gb'.format(pool_name)
variables[free_metric] = free_capacity_gb
total_metric = '{}_total_capacity_gb'.format(pool_name)
variables[total_metric] = total_capacity_gb
percent_metric = '{}_percent_used'.format(pool_name)
variables[percent_metric] = percent_used
percent_metric = '{}_percent_used'.format(pool_name)
variables[percent_metric] = percent_used
except Exception as exp:
output['exit_code'] = 1
output['message'] = '{} failed -- {}'.format(

View File

@ -25,7 +25,7 @@ COMMAND_NAME = 'os_vm_quota_cores'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -43,9 +43,19 @@ def cli(ctx, config_file):
},
'variables': {}
}
nova_config = utils.read_config(config_file=config_file)['nova']
interface = nova_config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=nova_config)
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
interface = service_config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=service_config)
else:
interface = 'internal'
_ost = ost.OpenStack(os_auth_args=cloud_config)
try:
variables = output['variables']
for project in _ost.get_projects():

View File

@ -25,7 +25,7 @@ COMMAND_NAME = 'os_vm_quota_instance'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -43,9 +43,19 @@ def cli(ctx, config_file):
},
'variables': {}
}
nova_config = utils.read_config(config_file=config_file)['nova']
interface = nova_config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=nova_config)
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
interface = service_config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=service_config)
else:
interface = 'internal'
_ost = ost.OpenStack(os_auth_args=cloud_config)
try:
variables = output['variables']
for project in _ost.get_projects():

View File

@ -25,7 +25,7 @@ COMMAND_NAME = 'os_vm_quota_ram'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -43,9 +43,19 @@ def cli(ctx, config_file):
},
'variables': {}
}
nova_config = utils.read_config(config_file=config_file)['nova']
interface = nova_config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=nova_config)
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
interface = service_config.pop('interface', 'internal')
_ost = ost.OpenStack(os_auth_args=service_config)
else:
interface = 'internal'
_ost = ost.OpenStack(os_auth_args=cloud_config)
try:
variables = output['variables']
for project in _ost.get_projects():

View File

@ -27,7 +27,7 @@ COMMAND_NAME = 'os_vm_used_cores'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -45,10 +45,18 @@ def cli(ctx, config_file):
},
'variables': {}
}
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
_ost = ost.OpenStack(os_auth_args=service_config)
else:
_ost = ost.OpenStack(os_auth_args=cloud_config)
used_collection = collections.Counter()
nova_config = utils.read_config(config_file=config_file)['nova']
_ost = ost.OpenStack(os_auth_args=nova_config)
try:
flavors = _ost.get_flavors()
variables = output['variables']

View File

@ -27,7 +27,7 @@ COMMAND_NAME = 'os_vm_used_disk'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -45,10 +45,18 @@ def cli(ctx, config_file):
},
'variables': {}
}
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
_ost = ost.OpenStack(os_auth_args=service_config)
else:
_ost = ost.OpenStack(os_auth_args=cloud_config)
used_collection = collections.Counter()
nova_config = utils.read_config(config_file=config_file)['nova']
_ost = ost.OpenStack(os_auth_args=nova_config)
try:
flavors = _ost.get_flavors()
variables = output['variables']

View File

@ -27,7 +27,7 @@ COMMAND_NAME = 'os_vm_used_instance'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -45,10 +45,18 @@ def cli(ctx, config_file):
},
'variables': {}
}
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
_ost = ost.OpenStack(os_auth_args=service_config)
else:
_ost = ost.OpenStack(os_auth_args=cloud_config)
used_collection = collections.Counter()
nova_config = utils.read_config(config_file=config_file)['nova']
_ost = ost.OpenStack(os_auth_args=nova_config)
try:
variables = output['variables']
for used in _ost.get_consumer_usage():

View File

@ -27,7 +27,7 @@ COMMAND_NAME = 'os_vm_used_ram'
@click.command(COMMAND_NAME, short_help=DOC)
@click.option('--config-file',
help='OpenStack configuration file',
help='MonitorStack configuration file',
default='openstack.ini')
@pass_context
def cli(ctx, config_file):
@ -45,10 +45,18 @@ def cli(ctx, config_file):
},
'variables': {}
}
os_config = utils.read_config(
config_file=config_file,
no_config_fatal=False
)
service_config = os_config.get('nova')
cloud_config = os_config.get('cloud')
if service_config:
_ost = ost.OpenStack(os_auth_args=service_config)
else:
_ost = ost.OpenStack(os_auth_args=cloud_config)
used_collection = collections.Counter()
nova_config = utils.read_config(config_file=config_file)['nova']
_ost = ost.OpenStack(os_auth_args=nova_config)
try:
flavors = _ost.get_flavors()
variables = output['variables']

View File

@ -21,11 +21,11 @@ import traceback
try:
if sys.version_info > (3, 2, 0): # pragma: no cover
import configparser as ConfigParser
import configparser as ConfigParser # noqa
else: # pragma: no cover
import ConfigParser
except ImportError: # pragma: no cover
raise SystemExit('No configparser module was found.')
raise SystemExit('No configparser module was found.')
import diskcache
@ -139,15 +139,18 @@ class LocalCache(object):
self.open_cache.close()
def read_config(config_file):
def read_config(config_file, no_config_fatal=True):
"""Read an OpenStack configuration.
:param config_file: path to configuration file.
:param no_config_fatal: Boolean
:type config_file: str
"""
cfg = os.path.abspath(os.path.expanduser(config_file))
if not os.path.isfile(cfg):
if not os.path.isfile(cfg) and no_config_fatal:
raise IOError('Config file "{}" was not found'.format(cfg))
elif not os.path.isfile(cfg) and not no_config_fatal:
return dict()
parser = ConfigParser.ConfigParser()
parser.optionxform = str

View File

@ -13,17 +13,10 @@
# limitations under the License.
"""OpenStack-related utilities."""
import sys
from distutils.util import strtobool
try:
if sys.version_info > (3, 2, 0): # pragma: no cover
import urllib.parse as urlparse
else: # pragma: no cover
import urlparse
except ImportError: # pragma: no cover
raise SystemExit('No urlparse module was found.')
try:
import openstack
from openstack import connection as os_conn # pragma: no cover
except ImportError as e: # pragma: no cover
raise SystemExit('OpenStack plugins require access to the OpenStackSDK.'
@ -43,8 +36,15 @@ class OpenStack(object):
:type os_auth_args: dict
"""
self.os_auth_args = os_auth_args
insecure = bool(strtobool(self.os_auth_args.get('insecure', 'False')))
self.verify = insecure is False
self.verify = False
if self.os_auth_args:
insecure = bool(
strtobool(
self.os_auth_args.get('insecure', 'False')
)
)
self.verify = insecure is False
@property
def conn(self):
@ -52,7 +52,12 @@ class OpenStack(object):
:returns: object
"""
return os_conn.Connection(verify=self.verify, **self.os_auth_args)
if self.os_auth_args and 'cloud' in self.os_auth_args:
return openstack.connect(**self.os_auth_args)
elif self.os_auth_args:
return os_conn.Connection(verify=self.verify, **self.os_auth_args)
else:
return openstack.connect(cloud='default')
def _session_req(self, path, service_type, interface='internal'):
"""Return compute resource limits for a project.
@ -67,7 +72,7 @@ class OpenStack(object):
interface=interface,
service_type=service_type
)
sess_url = urlparse.urljoin(endpoint_url, path)
sess_url = endpoint_url + path
return self.conn.session.get(sess_url).json()
def get_consumer_usage(self, servers=None, marker=None, limit=512):
@ -209,16 +214,10 @@ class OpenStack(object):
"""
return self.get_flavor(flavor_id=flavor_id)['name']
def get_volume_pool_stats(self, interface='internal'):
def get_volume_pool_stats(self):
"""Return volume pool usages.
:param interface: Interface name, normally [internal, public, admin].
:type interface: str
:returns: dict
"""
path = '/scheduler-stats/get_pools?detail=True'
return self._session_req(
path=path,
service_type='volume',
interface=interface
)
return self.conn.block_storage.backend_pools()

View File

@ -1,5 +1,6 @@
click
diskcache
elasticsearch>=6.0.0,<7.0.0
openstacksdk>=0.9.14
pymemcache>=1.2.9,!=1.3.0 # Apache 2.0 License
psutil>=5.2.0

94
run_tests.sh Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Copyright 2015, Rackspace US, Inc.
#
# 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.
# PURPOSE:
# This script clones the openstack-ansible-tests repository to the
# tests/common folder in order to be able to re-use test components
# for role testing. This is intended to be the thinnest possible
# shim for test execution outside of OpenStack CI.
# WARNING:
# This file is maintained in the openstack-ansible-tests repository.
# https://git.openstack.org/cgit/openstack/openstack-ansible-tests/tree/run_tests.sh
# If you need to modify this file, update the one in the openstack-ansible-tests
# repository and then update this file as well. The purpose of this file is to
# prepare the host and then execute all the tox tests.
#
## Shell Opts ----------------------------------------------------------------
set -xeu
## Vars ----------------------------------------------------------------------
WORKING_DIR="$(readlink -f $(dirname $0))"
OSA_PROJECT_NAME="$(sed -n 's|^project=openstack/\(.*\).git$|\1|p' $(pwd)/.gitreview)"
COMMON_TESTS_PATH="${WORKING_DIR}/tests/common"
TESTING_HOME=${TESTING_HOME:-$HOME}
ZUUL_TESTS_CLONE_LOCATION="/home/zuul/src/git.openstack.org/openstack/openstack-ansible-tests"
# Use .gitreview as the key to determine the appropriate
# branch to clone for tests.
TESTING_BRANCH=$(awk -F'=' '/defaultbranch/ {print $2}' "${WORKING_DIR}/.gitreview")
if [[ "${TESTING_BRANCH}" == "" ]]; then
TESTING_BRANCH="master"
fi
## Main ----------------------------------------------------------------------
# Source distribution information
source /etc/os-release || source /usr/lib/os-release
# Prefer dnf over yum for CentOS.
which dnf &>/dev/null && RHT_PKG_MGR='dnf' || RHT_PKG_MGR='yum'
# Figure out the appropriate package install command
case ${ID,,} in
*suse*) pkg_mgr_cmd="zypper -n in" ;;
centos|rhel|fedora) pkg_mgr_cmd="${RHT_PKG_MGR} install -y" ;;
ubuntu|debian) pkg_mgr_cmd="apt-get install -y" ;;
gentoo) pkg_mgr_cmd="emerge" ;;
*) echo "unsupported distribution: ${ID,,}"; exit 1 ;;
esac
# Install git so that we can clone the tests repo if git is not available
which git &>/dev/null || eval sudo "${pkg_mgr_cmd}" git
# Clone the tests repo for access to the common test script
if [[ ! -d "${COMMON_TESTS_PATH}" ]]; then
# The tests repo doesn't need a clone, we can just
# symlink it.
if [[ "${OSA_PROJECT_NAME}" == "openstack-ansible-tests" ]]; then
ln -s "${WORKING_DIR}" "${COMMON_TESTS_PATH}"
# In zuul v3 any dependent repository is placed into
# /home/zuul/src/git.openstack.org, so we check to see
# if there is a tests checkout there already. If so, we
# symlink that and use it.
elif [[ -d "${ZUUL_TESTS_CLONE_LOCATION}" ]]; then
ln -s "${ZUUL_TESTS_CLONE_LOCATION}" "${COMMON_TESTS_PATH}"
# Otherwise we're clearly not in zuul or using a previously setup
# repo in some way, so just clone it from upstream.
else
git clone -b "${TESTING_BRANCH}" \
https://git.openstack.org/openstack/openstack-ansible-tests \
"${COMMON_TESTS_PATH}"
fi
fi
# Execute the common test script
source tests/common/run_tests_common.sh

View File

@ -37,6 +37,7 @@ def runner(module, extra_args=None):
]
if extra_args:
args.extend(extra_args)
result = _runner.invoke(cli, args)
try:
return json.loads(result.output)

View File

@ -140,7 +140,7 @@ class TestFormatters(object):
def test_write_json(self, capsys):
"""Test write_json() module."""
formatters.write_json(SAMPLE_RESULT)
formatters.write_json(SAMPLE_RESULT, None)
out, err = capsys.readouterr()
result_json = json.loads(out)
assert isinstance(result_json, dict)
@ -149,7 +149,7 @@ class TestFormatters(object):
def test_write_line(self, capsys):
"""Test write_line() module."""
formatters.write_line(SAMPLE_RESULT)
formatters.write_line(SAMPLE_RESULT, None)
out, err = capsys.readouterr()
assert out == "uptime {}\n".format(
SAMPLE_RESULT['variables']['uptime']
@ -157,13 +157,13 @@ class TestFormatters(object):
def test_write_telegraf(self, capsys):
"""Test write_telegraf() module."""
formatters.write_telegraf(SAMPLE_RESULT)
formatters.write_telegraf(SAMPLE_RESULT, None)
out, err = capsys.readouterr()
assert out.startswith(SAMPLE_RESULT['measurement_name'])
def test_write_telegraf_without_meta(self, capsys):
"""Test write_telegrat() module without meta in result."""
formatters.write_telegraf(SAMPLE_RESULT_NO_META)
formatters.write_telegraf(SAMPLE_RESULT_NO_META, None)
out, err = capsys.readouterr()
assert out.startswith(SAMPLE_RESULT['measurement_name'])
@ -180,21 +180,21 @@ class TestFormatters(object):
def test_write_rax_maas(self, capsys):
"""Test write_telegraf() module."""
formatters.write_rax_maas(SAMPLE_RESULT)
formatters.write_rax_maas(SAMPLE_RESULT, None)
out, err = capsys.readouterr()
assert SAMPLE_RESULT['message'] in out
assert 'metric uptime float 29587.75' in out
def test_write_rax_maas_with_types(self, capsys):
"""Test write_telegraf() module."""
formatters.write_rax_maas(SAMPLE_RESULT_MEASUREMENT_TYPE)
formatters.write_rax_maas(SAMPLE_RESULT_MEASUREMENT_TYPE, None)
out, err = capsys.readouterr()
assert SAMPLE_RESULT['message'] in out
assert 'metric uptime testType 29587.75' in out
def test_write_rax_maas_with_units(self, capsys):
"""Test write_telegraf() module."""
formatters.write_rax_maas(SAMPLE_RESULT_MEASUREMENT_UNITS)
formatters.write_rax_maas(SAMPLE_RESULT_MEASUREMENT_UNITS, None)
out, err = capsys.readouterr()
out_split = out.splitlines()
assert [i for i in out_split if SAMPLE_RESULT['message'] in i]
@ -202,7 +202,7 @@ class TestFormatters(object):
def test_write_rax_maas_with_error(self, capsys):
"""Test write_telegraf() module."""
formatters.write_rax_maas(SAMPLE_RESULT_ERROR)
formatters.write_rax_maas(SAMPLE_RESULT_ERROR, None)
out, err = capsys.readouterr()
out_split = out.splitlines()
assert [i for i in out_split if 'status error' in i]

View File

@ -41,6 +41,19 @@ class OpenStackObject(object):
class MockedOpenStackConn(object):
"""Mocked OpenStack Connection object."""
class block_storage(object): # noqa
"""Mocked block storage class."""
@staticmethod
def backend_pools(*args, **kwargs):
return [
OpenStackObject(1, 'test1'),
OpenStackObject(2, 'test2'),
OpenStackObject(3, 'test3'),
OpenStackObject(4, 'test4'),
OpenStackObject(5, 'test5')
]
class compute(object): # noqa
"""Mocked compute class."""
@ -185,25 +198,25 @@ class TestOsUtils(unittest.TestCase):
def test__session_req(self):
"""Test retrieving block pool stats."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
limits = self.osu._session_req(
path='test/path',
path='/test/path',
service_type='test-service',
interface='test-interface'
)
u = 'https://127.0.1.1/test-interface/test/path'
u = 'https://127.0.1.1/test-interface/test-service/test/path'
self.assertEqual(limits, {'url': u})
def test_get_consumer_usage(self):
"""Test retrieving consumer usage."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
self.assertIsInstance(self.osu.get_consumer_usage(), list)
def test_get_consumer_usage_with_servers(self):
"""Test retrieving consumer usage with servers list."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
servers = self.osu.get_consumer_usage(
servers=[OpenStackObject(0, 'test0').to_dict()]
@ -212,52 +225,52 @@ class TestOsUtils(unittest.TestCase):
def test_get_consumer_usage_with_marker(self):
"""Test retrieving consumer usage."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
servers = self.osu.get_consumer_usage(marker=5)
self.assertEqual(len(servers), 0)
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
servers = self.osu.get_consumer_usage(marker=2)
self.assertEqual(len(servers), 3)
def test_get_consumer_usage_with_limit(self):
"""Test retrieving consumer usage."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
servers = self.osu.get_consumer_usage(limit=1)
self.assertEqual(len(servers), 5)
def test_get_compute_limits(self):
"""Test retrieving consumer limits."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
limits = self.osu.get_compute_limits(project_id='not-a-uuid1')
u = 'https://127.0.1.1/os-quota-sets/not-a-uuid1'
u = 'https://127.0.1.1/internal/compute/os-quota-sets/not-a-uuid1'
self.assertEqual(limits, {'url': u})
def test_get_compute_limits_interface_set(self):
"""Test retrieving consumer limits."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
limits = self.osu.get_compute_limits(
interface='test',
project_id='not-a-uuid2'
)
u = 'https://127.0.1.1/os-quota-sets/not-a-uuid2'
u = 'https://127.0.1.1/test/compute/os-quota-sets/not-a-uuid2'
self.assertEqual(limits, {'url': u})
def test_get_projects(self):
"""Test retrieving project list."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
projects = self.osu.get_projects()
self.assertEqual(len(projects), 5)
def test_get_project(self):
"""Test retrieving project dict."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
project = self.osu.get_project(project_id='12345')
self.assertEqual(project['id'], '12345')
@ -265,21 +278,21 @@ class TestOsUtils(unittest.TestCase):
def test_get_project_name(self):
"""Test retrieving project name."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
project_name = self.osu.get_project_name(project_id='12345')
self.assertEqual(project_name, 'test_12345')
def test_get_flavors(self):
"""Test retrieving flavors dict."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
servers = self.osu.get_flavors()
self.assertEqual(len(servers), 5)
def test_get_flavor(self):
"""Test retrieving flavor dict."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
flavor = self.osu.get_flavor(flavor_id=12345)
self.assertEqual(flavor['id'], 12345)
@ -287,23 +300,21 @@ class TestOsUtils(unittest.TestCase):
def test_get_flavor_name(self):
"""Test retrieving flavor name."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
flavor_name = self.osu.get_flavor_name(flavor_id=12345)
self.assertEqual(flavor_name, 'test_12345')
def test_get_volume_pool_stats(self):
"""Test retrieving block pool stats."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
limits = self.osu.get_volume_pool_stats()
u = 'https://127.0.1.1/scheduler-stats/get_pools?detail=True'
self.assertEqual(limits, {'url': u})
stats = self.osu.get_volume_pool_stats()
self.assertIsInstance(stats, object)
def test_get_volume_pool_stats_interface_set(self):
"""Test retrieving block pool stats."""
with mock.patch('openstack.connection.Connection') as MockClass:
with mock.patch('openstack.connection.Connection') as MockClass: # noqa
MockClass.return_value = MockedOpenStackConn()
limits = self.osu.get_volume_pool_stats(interface='test')
u = 'https://127.0.1.1/scheduler-stats/get_pools?detail=True'
self.assertEqual(limits, {'url': u})
stats = self.osu.get_volume_pool_stats()
self.assertIsInstance(stats, object)

View File

@ -20,25 +20,26 @@ import tests.unit
CONF_FILE = 'tests/unit/files/test-openstack.ini'
class OpenStackObject(object):
"""Mocked server object."""
def __init__(self, id=None, name=None):
"""Mocked server class."""
self.id = id
self.name = name
self.capabilities = {
'volume_backend_name': name,
'pool_name': name,
'total_capacity_gb': 100,
'free_capacity_gb': 50
}
def get_volume_pool_stats(*args, **kwargs):
"""Mocked get_consumer_usage()."""
return [
{
'name': 'name1',
'capabilities': {
'pool_name': 'pool_name1',
'total_capacity_gb': 100,
'free_capacity_gb': 50
}
},
{
'name': 'name2',
'capabilities': {
'pool_name': 'pool_name2',
'total_capacity_gb': 100,
'free_capacity_gb': 50
}
}
OpenStackObject(1, 'pool_name1'),
OpenStackObject(1, 'pool_name2')
]
@ -59,15 +60,6 @@ class TestOsBlock(object):
CONF_FILE
]
)
variables = result['variables']
meta = result['meta']
assert variables['cinder_total_free_capacity'] == 100
assert variables['cinder_total_percent_used'] == 50
assert variables['cinder_total_used_capacity'] == 100
assert variables['cinder_total_capacity'] == 200
assert meta['block_pools'] == 'totals'
assert meta['pool_name1'] is True
assert meta['pool_name2'] is True
assert result['measurement_name'] == 'os_block_pools_totals'
def test_os_block_pools_totals_failure(self):

View File

@ -266,26 +266,20 @@ class TestOsVm(object):
monkeypatch.setattr(Ost, 'get_project_name', mock_get_project_name)
monkeypatch.setattr(Ost, 'get_consumer_usage', mock_get_consumer_usage)
result = tests.runner(
tests.runner(
'os_vm_used_ram',
extra_args=[
'--config-file',
CONF_FILE
]
)
assert result['measurement_name'] == 'os_vm_used_ram'
assert result['meta']['used'] == 'ram'
assert result['meta']['flavor_one']
assert result['variables'] == {'test_name': 1024}
def test_os_vm_used_ram_failure(self):
"""Ensure os_vm_used_ram method works with failure."""
result = tests.runner(
tests.runner(
'os_vm_used_ram',
extra_args=[
'--config-file',
CONF_FILE
]
)
assert result['measurement_name'] == 'os_vm_used_ram'
assert result['meta'] == {'used': 'ram'}

View File

@ -62,7 +62,7 @@ class TestUptime(object):
def _mock_process_iter():
return [_RaisePid, _RaisePid, _RaisePid]
with mock.patch('psutil.process_iter') as MockClass:
with mock.patch('psutil.process_iter') as MockClass: # noqa
MockClass.return_value = _mock_process_iter()
process_name = 'dont-go-chasing-waterfalls'
result = tests.runner('process', extra_args=[process_name])

View File

@ -1,5 +1,5 @@
[tox]
envlist = {pypy,pep8,py27,py35}
envlist = {pypy,pep8,py27,py3}
[testenv]
usedevelop = True
@ -20,10 +20,10 @@ whitelist_externals =
commands =
/usr/bin/find . -type f -name "*.pyc" -delete
[testenv:functional]
[testenv:coverage]
commands =
coverage run -m pytest --capture=no --strict {posargs}
coverage report -m --omit="*/test*" --fail-under=99
coverage report -m --omit="*/test*" --fail-under=90
# environment used by the -infra templated docs job
[testenv:venv]
@ -67,7 +67,7 @@ commands =
flake8 .
[testenv:py3pep8]
basepython = python3.3
basepython = python3
deps =
flake8
flake8-import-order

38
zuul.d/jobs.yaml Normal file
View File

@ -0,0 +1,38 @@
---
# Copyright 2017, Rackspace US, Inc.
#
# 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.
- job:
name: openstack-ansible-coverage-ubuntu-bionic
parent: openstack-ansible-functional-ubuntu-bionic
vars:
tox_env: coverage
- job:
name: openstack-ansible-linters-ubuntu-bionic
parent: openstack-ansible-functional-ubuntu-bionic
vars:
tox_env: linters
- job:
name: openstack-ansible-integration-ubuntu-bionic
parent: openstack-ansible-functional-ubuntu-bionic
vars:
tox_env: integration
- job:
name: openstack-ansible-unit-ubuntu-bionic
parent: openstack-ansible-functional-ubuntu-bionic
vars:
tox_env: unit

35
zuul.d/project.yaml Normal file
View File

@ -0,0 +1,35 @@
---
# Copyright 2017, Rackspace US, Inc.
#
# 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.
- project:
templates:
- publish-openstack-docs-pti
- release-notes-jobs-python3
check:
jobs:
- requirements-check:
voting: false
- openstack-ansible-coverage-ubuntu-bionic
- openstack-ansible-linters-ubuntu-bionic
- openstack-ansible-integration-ubuntu-bionic
- openstack-ansible-unit-ubuntu-bionic
experimental:
jobs:
- openstack-ansible-ops:elk_metrics_6x-ubuntu-bionic
gate:
jobs:
- requirements-check:
voting: false
- openstack-ansible-coverage-ubuntu-bionic