Retire this project

Change-Id: I029ade3530639afcc480b0c5a6f1bdc7e5711528
This commit is contained in:
Martin Mágr 2016-06-03 10:12:13 +02:00
parent a156c2367e
commit f68777dbe5
33 changed files with 11 additions and 3097 deletions

7
.gitignore vendored
View File

@ -1,7 +0,0 @@
.tox
AUTHORS
ChangeLog
*.egg-info
*.pyc
.testrepository
doc/build

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=stackforge/monitoring-for-openstack.git

View File

@ -1,4 +0,0 @@
[DEFAULT]
test_command=${PYTHON:-python} -m subunit.run discover ./oschecks/tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

191
LICENSE
View File

@ -1,191 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -1,23 +1,17 @@
monitoring-for-openstack
========================
A bunch of scripts that you can use to monitor OpenStack Services
This project is no longer maintained.
License
=======
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".
Apache License 2.0
(Optional:)
For an alternative project, please see osops-tools-monitoring at
https://git.openstack.org/openstack/osops-tools-monitoring .
Copyright 2012-2014 eNovance <licensing@enovance.com>
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.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@ -1,258 +0,0 @@
# -*- coding: utf-8 -*-
#
# monitoring-for-openstack documentation build configuration file, created by
# sphinx-quickstart on Tue Oct 28 18:03:41 2014.
#
# 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.
import sys
import os
# 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 = []
# 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.
project = u'monitoring-for-openstack'
copyright = u'2014, eNovance'
# 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.
version = '1'
# The full version, including alpha/beta/rc tags.
release = '1'
# 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 = 'sphinx'
# 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 = 'default'
# 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 = 'monitoring-for-openstackdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# 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', 'monitoring-for-openstack.tex', u'monitoring-for-openstack Documentation',
u'eNovance', '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', 'monitoring-for-openstack', u'monitoring-for-openstack Documentation',
[u'eNovance'], 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', 'monitoring-for-openstack', u'monitoring-for-openstack Documentation',
u'eNovance', 'monitoring-for-openstack', '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

View File

@ -1,22 +0,0 @@
.. monitoring-for-openstack documentation master file, created by
sphinx-quickstart on Tue Oct 28 18:03:41 2014.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to monitoring-for-openstack's documentation!
====================================================
Contents:
.. toctree::
:maxdepth: 2
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,114 +0,0 @@
#!/bin/bash
#
# Nova create instance monitoring script for Sensu / Nagios
#
# Copyright 2014 eNovance <licensing@enovance.com>
#
# Author: Florian Lambert <florian.lambert@enovance.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Requirement: curl
#
# Nagios/Sensu return codes
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4
# Script options
usage ()
{
echo "Usage: $0 [OPTIONS]"
echo " -h Get help"
echo " -E <Endpoint URL> URL for horizon dashboard. Ex: http://os.enocloud.com"
echo " -U <username> Username to use to get an auth token"
echo " -P <password> Password to use ro get an auth token"
echo " -c <cookieFile> Temporaire file to store cookie. Ex: /tmp/check_horizon_cookieFile"
}
output_result () {
# Output check result & refresh cache if requested
MSG="$1"
RETCODE=$2
echo "$MSG"
exit $RETCODE
}
while getopts 'hH:U:P:E:c' OPTION
do
case $OPTION in
h)
usage
exit 0
;;
E)
export HORIZON_URL=$OPTARG
;;
U)
export OS_USERNAME=$OPTARG
;;
P)
export OS_PASSWORD=$OPTARG
;;
c)
export COOKIE_FILE=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
COOKIE_FILE=${COOKIE_FILE:-"/tmp/check_horizon_cookieFile"}
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ] || [ -z "$HORIZON_URL" ]
then
usage
exit 1
fi
if ! which curl > /dev/null 2>&1
then
output_result "UNKNOWN - curl is not installed." $STATE_UNKNOWN
fi
# Get CSRFTOKEN and REGION on index
GET_INDEX=$(curl -s -c $COOKIE_FILE -i $HORIZON_URL)
if [ -z "$GET_INDEX" ]
then
output_result "CRITICAL - $HORIZON_URL not respond." $STATE_CRITICAL
fi
CSRFTOKEN=$(echo $GET_INDEX | sed -r "s/.*csrfmiddlewaretoken['\"] value=['\"]([^'\"]+)['\"].*/\1/")
REGION=$(echo $GET_INDEX | sed -r "s/.*region['\"] value=['\"]([^\"']+)['\"].*/\1/")
# Send POST login with CSRFTOKEN and REGION on /auth/login/
RESULT=$(curl -s -L -b "$COOKIE_FILE" --referer $HORIZON_URL --data "username=$OS_USERNAME&password=$OS_PASSWORD&region=$REGION&csrfmiddlewaretoken=$CSRFTOKEN" $HORIZON_URL/auth/login/)
# If Auth work, find patterns Overview
if [ ` echo $RESULT | grep OpenStack | wc -l ` -ne 0 ]
then
output_result "OK - Found string OpenStack in $HORIZON_URL" $STATE_OK
else
output_result "CRITICAL - String \"Overview\" not found in $HORIZON_URL" $STATE_CRITICAL
fi

View File

@ -1,122 +0,0 @@
#!/bin/bash
#
# Swift monitoring script for Nagios
#
# Copyright 2012 eNovance <licensing@enovance.com>
#
# Author: Julien Danjou <julien@danjou.info>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4
usage ()
{
echo "Usage: $0 [OPTIONS]"
echo " -h Get help"
echo " -A <url> URL for obtaining an auth token"
echo " -U <username> Username to use to get an auth token"
echo " -K <key> Password to use to get an auth token"
echo " -T <tenant> Tenant name to use"
echo " -V <authversion> Version for authentication"
echo " -c <container> Container to upload to"
echo " -s <maxsize> Determine maximum file size in KB"
echo " (default: 1024)"
}
while getopts 'hH:A:U:K:T:V:c:s:' OPTION
do
case $OPTION in
h)
usage
exit 0
;;
A)
export OS_AUTH_URL=$OPTARG
;;
U)
export OS_USERNAME=$OPTARG
;;
K)
export OS_PASSWORD=$OPTARG
;;
T)
export OS_TENANT_NAME=$OPTARG
;;
V)
export ST_AUTH_VERSION=$OPTARG
;;
c)
container=$OPTARG
;;
s)
multi=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
multi=${multi:-1024}
container=${container:-check_swift}
if ! which swift >/dev/null 2>&1
then
echo "Swift command not found"
exit $STATE_UNKNOWN
fi
delete_files ()
{
test -n "$KEY" && swift delete "$container" "$KEY" >/dev/null 2>&1 || true
rm -f "$TMPFILE" "$TMPFILE_TARGET"
}
trap delete_files EXIT
TMPFILE=`mktemp`
BLOCK_NUMBER=$(($RANDOM * $multi / 32767))
BLOCK_SIZE=1024
dd if=/dev/urandom of=$TMPFILE count=$BLOCK_NUMBER bs=$BLOCK_SIZE >/dev/null 2>&1
TMPFILE_TARGET=`mktemp`
if ! KEY=$(swift upload "$container" "$TMPFILE" 2>/dev/null)
then
echo "Unable to upload file"
exit $STATE_CRITICAL
fi
if ! swift download "$container" "$KEY" -o "$TMPFILE_TARGET" >/dev/null 2>&1
then
echo "File upload OK, but unable to download file"
exit $STATE_CRITICAL
fi
if ! swift delete "$container" "$KEY" >/dev/null 2>&1
then
echo "File upload+download OK, but unable to delete uploaded file"
exit $STATE_CRITICAL
fi
echo "Upload+download+delete of $(($BLOCK_NUMBER * $BLOCK_SIZE / 1024)) KiB file in container $container"

View File

@ -1,59 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#
# Swift dispersion monitoring script for Nagios
#
# Copyright © 2012 eNovance <licensing@enovance.com>
#
# Author: Julien Danjou <julien@danjou.info>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import sys
import json
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4
with os.popen("swift-dispersion-report -j %s" \
% os.getenv("SWIFT_DISPERSION_CONFIG", "/etc/swift/dispersion.conf")) as report:
stats = json.load(report)
msgs = []
state = STATE_OK
# type_ is either "objects", "container"
for type_, values in stats.iteritems():
msgs.append("%.2f%% %ss found" % (values['pct_found'], type_))
if values.get('missing_one', 0)> 0:
msgs.append("%d %ss missing one copy" % (values['missing_one'], type_))
state = max(state, STATE_WARNING)
if values.get('missing_two', 0) > 0:
msgs.append("%d %ss missing two copies" % (values['missing_two'], type_))
state = max(state, STATE_WARNING)
if values.get('missing_all', 0) > 0:
msgs.append("%d %ss missing ALL copies" % (values['missing_all'], type_))
state = max(state, STATE_CRITICAL)
print ", ".join(msgs)
sys.exit(state)

View File

@ -1,70 +0,0 @@
#!/bin/sh
#
# check_swift_object_servers - Check OpenStack Swift object servers status
#
# Copyright 2012 eNovance <licensing@enovance.com>
#
# Author: Julien Danjou <julien@danjou.info>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
PROGNAME=`basename $0`
REVISION="1.0"
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4
print_usage() {
echo "Usage: $PROGNAME"
}
print_help() {
print_usage
echo "This plugin checks Swift status using the swift-recon program."
exit 0
}
case "$1" in
--help|-h)
print_help
exit 0
;;
esac
if ! which swift-recon >/dev/null 2>&1
then
echo "swift-recon command not found"
exit $STATE_UNKNOWN
fi
CHECK=$(swift-recon --md5 | grep ' error')
echo $CHECK
if echo "$CHECK" | grep -q ' 0 error'
then
exit $STATE_OK
elif echo "$CHECK" | grep -q ' 1 error'
then
exit $STATE_WARNING
else
exit $STATE_CRITICAL
fi

View File

@ -1,135 +0,0 @@
#!/bin/bash
#
# check_swift_recon - Check OpenStack Swift recon values
#
# Copyright 2012 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
PROGNAME=`basename $0`
REVISION="1.0"
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4
print_usage() {
echo "Usage: $PROGNAME [--field|-f] FIELD [[--critical|-c] VALUE [--warning|-w] VALUE]"
}
print_help() {
print_usage
echo "This plugin checks Swift status using the swift-recon program."
exit 1
}
while [ "$1" ]; do
case "$1" in
--help|-h)
print_help
exit 0
;;
--field|-f)
field=$2
shift ; shift
;;
--critical|-c)
critical=$2
shift ; shift
;;
--warning|-w)
warning=$2
shift ; shift
;;
esac
done
[ ! "$field" ] && print_help
if ! which swift-recon >/dev/null 2>&1
then
echo "swift-recon command not found"
exit $STATE_UNKNOWN
fi
case $field in
async_pending)
opt="--async"
no_hosts_data_is_ok=1
;;
replication_time)
opt="--replication"
;;
ALL_auditor|ZBF_auditor)
opt="--auditor"
;;
updater_last_sweep)
opt="--updater"
;;
object_expiration_pass|expired_last_pass)
opt="--expirer"
;;
quarantined_objects|quarantined_accounts|quarantined_containers)
opt="--quarantined"
;;
orphan|tcp_in_use|time_wait|tcp6_in_use|tcp_mem_allocated_bytes)
opt="--sockstat"
;;
esac
data=$(swift-recon $opt | sed -n 's/^\['$field'\] //gp')
eval $(echo $data | sed -n 's/^low: \([[:digit:]\.]*\), high: \([[:digit:]\.]*\), avg: \([[:digit:]\.]*\), total: \([[:digit:]\.]*\), Failed: \([[:digit:]\.]*\)%, no_result: \([[:digit:]\.]*\), reported: \([[:digit:]\.]*\)/low="\1";high="\2";avg="\3";total="\4";failed="\5";no_result="\6";reported="\7"/gp')
if [ "$(echo $data | grep 'No hosts returned valid data')" ] ; then
no_host_data=1
else
failed=$(( ${failed/./} / 10 )) # change percent in perthousand
avg=$(( ${avg/./} / 10 )) # change percent in perthousand
fi
if [ "$no_host_data" ]; then
if [ "$no_hosts_data_is_ok" ]; then
echo "OK - No data"
exit $STATE_OK
else
echo "CRITICAL - $data"
exit $STATE_CRITICAL
fi
elif [ "$no_result" != "0" ]; then
echo "CRITICAL - $data"
exit $STATE_CRITICAL
elif [ "$failed" != "0" ]; then
echo "CRITICAL - $data"
exit $STATE_CRITICAL
elif [ -n "$critical" -a -n "$warning" ]; then
if [ $avg -ge $warning -a $avg -lt $critical ]; then
echo "WARNING - $data"
exit $STATE_WARNING
elif [ $avg -ge $critical ]; then
echo "CRITICAL - $data"
exit $STATE_CRITICAL
fi
fi
echo "OK - $data"
exit $STATE_OK

View File

@ -1,91 +0,0 @@
#!/bin/bash
#
# Check the disk usage of the Swift ring
#
# Copyright 2014 eNovance <licensing@enovance.com>
#
# Author: Gaetan Trellu <gaetan.trellu@enovance.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Requirement : swift-recon
#
set -e
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
RECON_BINARY=/usr/bin/swift-recon
function usage() {
echo "This plugin checks the ring disk usage by using the swift-recon command."
echo
echo "Usage: `basename $0` [OPTIONS]"
echo " -h Display this help"
echo " -c Set the critical limit in % for the ring usage. Ex: 95"
echo " -w Set the warning limit in % for the ring usage. Ex: 90"
echo
}
while getopts 'w:c:h' OPTION
do
case $OPTION in
h)
usage
exit 0
;;
c)
export CRITICAL=$OPTARG
;;
w)
export WARNING=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
#Default values
CRITICAL=${CRITICAL:-"90"}
WARNING=${WARNING:-"80"}
if [ ! -x $RECON_BINARY ]
then
echo "CRITICAL: Unable to use the swift-recon binary"
exit $STATE_CRITICAL
else
RING_USAGE=$(swift-recon -d | grep "avg" | awk '{ print $NF }' | cut -d"." -f1)
if [ "$RING_USAGE" -ge "$WARNING" -a "$RING_USAGE" -lt "$CRITICAL" ]
then
echo "WARNING: $RING_USAGE% space used"
exit $STATUS_WARNING
fi
if [ "$RING_USAGE" -ge "$CRITICAL" ]
then
echo "CRITICAL: $RING_USAGE% space used"
exit $STATUS_CRITICAL
else
echo "OK: $RING_USAGE% space used"
exit $STATUS_OK
fi
fi

View File

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 argparse
from oschecks import utils
def check_amqp():
parser = argparse.ArgumentParser(
description='Check amqp connection of an OpenStack service.')
parser.add_argument(dest='process_name',
help='Process name')
options = parser.parse_args()
utils.check_process_exists_and_amqp_connected(options.process_name)
def main():
utils.safe_run(check_amqp)

View File

@ -1,51 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 oschecks import utils
def _check_ceilometer_api():
ceilometer = utils.Ceilometer()
ceilometer.add_argument('-w', dest='warning', type=int, default=5,
help='Warning timeout for Ceilometer APIs calls')
ceilometer.add_argument('-c', dest='critical', type=int, default=10,
help='Critical timeout for Ceilometer APIs calls')
options, client = ceilometer.setup()
elapsed, meters = utils.timeit(client.meters.list)
if not meters:
utils.critical("Unable to contact Ceilometer API.")
if elapsed > options.critical:
utils.critical("Get meters took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.critical, elapsed))
elif elapsed > options.warning:
utils.warning("Get meters took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.warning, elapsed))
else:
utils.ok("Get meters, Ceilometer API is working: "
"list %d meters in %d seconds.|response_time=%d" %
(len(meters), elapsed, elapsed))
def check_ceilometer_api():
utils.safe_run(_check_ceilometer_api)

View File

@ -1,120 +0,0 @@
#!/usr/bin/env python
#
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
#
# Author: Frederic Lepied <frederic.lepied@enovance.com>
#
# 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.
''' Nagios check using ceph health.
'''
import json
import subprocess
import sys
import traceback
def per(percent, value):
return percent / 100 * value
def remaning(avail, total):
return "(%dMB/%dMB)" % (avail/1024, total/1024)
def interpret_output_df(output):
'''Parse the output of ceph health.
Return an exit code and message compatible with nagios.
'''
try:
data = json.loads(output)
except Exception:
return (1, 'CEPH WARNING: unable to parse ceph df %s' %
traceback.format_exc())
warn_percent = int(sys.argv[1]) if len(sys.argv) >= 2 else 85
crit_percent = int(sys.argv[2]) if len(sys.argv) >= 3 else 98
total = int(data['stats']['total_space'])
used = int(data['stats']['total_used'])
avail = int(data['stats']['total_avail'])
# Test correctness of values
if used + avail != total:
return (1, '[WARN] Used + Avail. != Total space')
elif avail < per(crit_percent, total):
return (2, "[ERR] Ceph df avail. critical %s" % remaning(avail, total))
elif avail < per(warn_percent, total):
return (1, "[WARN] Ceph df avail. waring %s" % remaning(avail, total))
else:
return (0, "[OK] Ceph df avail. seems good %s" %
remaning(avail, total))
def check_ceph_df():
'Program entry point.'
try:
res = subprocess.check_output(["ceph", "df", "--format=json"],
stderr=subprocess.STDOUT)
exit_code, message = interpret_output_df(res)
sys.stdout.write("%s\n" % message)
sys.exit(exit_code)
except subprocess.CalledProcessError as e:
sys.stdout.write('CEPH UNKNOWN: %s\n' % e.output)
sys.exit(3)
except OSError:
sys.stdout.write('CEPH UNKNOWN: unable to launch ceph health\n')
sys.exit(3)
def interpret_output_health(output):
'''Parse the output of ceph health.
Return an exit code and message compatible with nagios.
'''
tokens = output.split(' ')
if len(tokens) == 1:
tokens[0] = tokens[0].strip()
tokens.append('\n')
if tokens[0] == 'HEALTH_OK':
return (0, 'CEPH OK: ' + ' '.join(tokens[1:]))
elif tokens[0] == 'HEALTH_WARN':
return (1, 'CEPH WARNING: ' + ' '.join(tokens[1:]))
elif tokens[0] == 'HEALTH_ERR':
return (2, 'CEPH CRITICAL: ' + ' '.join(tokens[1:]))
else:
return (3, 'CEPH UNKNOWN: ' + ' '.join(tokens))
def check_ceph_health():
'Program entry point.'
try:
res = subprocess.check_output(["ceph", "health"],
stderr=subprocess.STDOUT)
exit_code, message = interpret_output_health(res)
sys.stdout.write(message)
sys.exit(exit_code)
except subprocess.CalledProcessError as e:
sys.stdout.write('CEPH UNKNOWN: %s\n' % e.output)
sys.exit(3)
except OSError:
sys.stdout.write('CEPH UNKNOWN: unable to launch ceph health\n')
sys.exit(3)

View File

@ -1,327 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 argparse
import datetime
import logging
import os
import time
import urlparse
from cinderclient.client import Client # noqa
from cinderclient import exceptions
from oschecks import utils
def _check_cinder_api():
cinder = utils.Cinder()
cinder.add_argument('-w', dest='warning', type=int, default=5,
help='Warning timeout for cinder APIs calls')
cinder.add_argument('-c', dest='critical', type=int, default=10,
help='Critical timeout for cinder APIs calls')
options, args, client = cinder.setup()
def quotas_list():
return client.quotas.get(options.os_tenant_name)
elapsed, quotas = utils.timeit(quotas_list)
if not quotas:
utils.critical("Unable to contact cinder API.")
if elapsed > options.critical:
utils.critical("Get quotas took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.critical, elapsed))
elif elapsed > options.warning:
utils.warning("Get quotas took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.warning, elapsed))
else:
utils.ok("Get quotas, cinder API is working: "
"list quota in %d seconds.|response_time=%d" %
(elapsed, elapsed))
def check_cinder_api():
utils.safe_run(_check_cinder_api)
class Novautils(object):
DAEMON_DEFAULT_PORT = 8776
def __init__(self, nova_client):
self.nova_client = nova_client
self.msgs = []
self.start = self.totimestamp()
self.notifications = ["volume_creation_time=%s" % self.start]
self.volume = None
self.connection_done = False
# python has no "toepoch" method: http://bugs.python.org/issue2736
# now, after checking http://stackoverflow.com/a/16307378,
# and http://stackoverflow.com/a/8778548 made my mind to this approach
@staticmethod
def totimestamp(dt=None, epoch=datetime.datetime(1970, 1, 1)):
if not dt:
dt = datetime.datetime.utcnow()
td = dt - epoch
# return td.total_seconds()
return int((td.microseconds +
(td.seconds + td.days * 24 * 3600) * 10**6) / 1e6)
def check_connection(self, force=False):
if not self.connection_done or force:
try:
# force a connection to the server
self.connection_done = self.nova_client.limits.get()
except Exception as e:
utils.critical("Cannot connect to cinder: %s" % e)
def get_duration(self):
return self.totimestamp() - self.start
def mangle_url(self, url):
# This first connection populate the structure we need inside
# the object. This does not cost anything if a connection has
# already been made.
self.check_connection()
try:
endpoint_url = urlparse.urlparse(url)
except Exception as e:
utils.unknown("you must provide an endpoint_url in the form"
+ "<scheme>://<url>/ (%s)" % e)
scheme = endpoint_url.scheme
if scheme is None:
utils.unknown("you must provide an endpoint_url in the form"
+ "<scheme>://<url>/ (%s)" % e)
catalog_url = None
try:
catalog_url = urlparse.urlparse(
self.nova_client.client.management_url)
except Exception as e:
utils.unknown("unknown error parsing the catalog url : %s" % e)
port = endpoint_url.port
if port is None:
if catalog_url.port is None:
port = self.DAEMON_DEFAULT_PORT
else:
port = catalog_url.port
netloc = "%s:%i" % (endpoint_url.hostname, port)
url = urlparse.urlunparse([scheme,
netloc,
catalog_url.path,
catalog_url.params,
catalog_url.query,
catalog_url.fragment])
self.nova_client.client.management_url = url
def check_existing_volume(self, volume_name, delete):
count = 0
for s in self.nova_client.volumes.list():
if s.display_name == volume_name:
if delete:
# asynchronous call, we do not check that it worked
s.delete()
count += 1
if count > 0:
if delete:
self.notifications.append("Found '%s' present %d time(s)"
% (volume_name, count))
else:
self.msgs.append("Found '%s' present %d time(s). "
% (volume_name, count)
+ "Won't create test volume. "
+ "Please check and delete.")
def create_volume(self, volume_name, size, availability_zone, volume_type):
if not self.msgs:
try:
conf = {'display_name': volume_name,
'size': size}
if volume_type:
conf['volume_type'] = volume_type
if availability_zone:
conf['availability_zone'] = availability_zone
self.volume = self.nova_client.volumes.create(**conf)
except Exception as e:
self.msgs.append("Cannot create the volume %s (%s)"
% (volume_name, e))
def volume_ready(self, timeout):
if not self.msgs:
timer = 0
while self.volume.status != "available":
if timer >= timeout:
self.msgs.append("Cannot create the volume.")
break
time.sleep(1)
timer += 1
try:
self.volume.get()
except Exception as e:
self.msgs.append("Problem getting the status of "
+ "the volume: %s" % e)
break
def delete_volume(self):
if not self.msgs or self.volume is not None:
try:
self.volume.delete()
except Exception as e:
self.msgs.append("Problem deleting the volume: %s" % e)
def volume_deleted(self, timeout):
deleted = False
timer = 0
while not deleted and not self.msgs:
time.sleep(1)
if timer >= timeout:
self.msgs.append("Could not delete the volume within"
+ "%d seconds" % timer)
break
timer += 1
try:
self.volume.get()
except exceptions.NotFound:
deleted = True
except Exception as e:
self.msgs.append("Cannot delete the volume (%s)" % e)
break
def _check_cinder_volume():
parser = argparse.ArgumentParser(
description='Check an OpenStack Keystone server.')
parser.add_argument('--auth_url', metavar='URL', type=str,
default=os.getenv('OS_AUTH_URL'),
help='Keystone URL')
parser.add_argument('--username', metavar='username', type=str,
default=os.getenv('OS_USERNAME'),
help='username to use for authentication')
parser.add_argument('--password', metavar='password', type=str,
default=os.getenv('OS_PASSWORD'),
help='password to use for authentication')
parser.add_argument('--tenant', metavar='tenant', type=str,
default=os.getenv('OS_TENANT_NAME'),
help='tenant name to use for authentication')
parser.add_argument('--endpoint_url', metavar='endpoint_url', type=str,
help='Override the catalog endpoint.')
parser.add_argument('--endpoint_type', metavar='endpoint_type', type=str,
default="publicURL",
help='Endpoint type in the catalog request. '
+ 'Public by default.')
parser.add_argument('--force_delete', action='store_true',
help='If matching volumes are found, delete them and '
+ 'add a notification in the message instead of '
+ 'getting out in critical state.')
parser.add_argument('--api_version', metavar='api_version', type=str,
default='1',
help='Version of the API to use. 1 by default.')
parser.add_argument('--timeout', metavar='timeout', type=int,
default=120,
help='Max number of second to create/delete a volume '
+ '(120 by default).')
parser.add_argument('--volume_name', metavar='volume_name', type=str,
default="monitoring_test",
help='Name of the volume to create '
+ '(monitoring_test by default)')
parser.add_argument('--volume_size', metavar='volume_size', type=int,
default=1,
help='Size of the volume to create (1 GB by default)')
parser.add_argument('--volume_type', metavar='volume_type', type=str,
default=None,
help='With multiple backends, choose the volume type.')
parser.add_argument('--availability_zone', metavar='availability_zone',
type=str,
default=None,
help='Specify availability zone.')
parser.add_argument('--verbose', action='count',
help='Print requests on stderr.')
args = parser.parse_args()
# this shouldn't raise any exception as no connection is done when
# creating the object. But It may change, so I catch everything.
try:
nova_client = Client(args.api_version,
username=args.username,
project_id=args.tenant,
api_key=args.password,
auth_url=args.auth_url,
endpoint_type=args.endpoint_type,
http_log_debug=args.verbose)
except Exception as e:
utils.critical("Error creating cinder communication object: %s" % e)
util = Novautils(nova_client)
if args.verbose:
ch = logging.StreamHandler()
nova_client.client._logger.setLevel(logging.DEBUG)
nova_client.client._logger.addHandler(ch)
# Initiate the first connection and catch error.
util.check_connection()
if args.endpoint_url:
util.mangle_url(args.endpoint_url)
# after mangling the url, the endpoint has changed. Check that
# it's valid.
util.check_connection(force=True)
util.check_existing_volume(args.volume_name, args.force_delete)
util.create_volume(args.volume_name,
args.volume_size,
args.availability_zone,
args.volume_type)
util.volume_ready(args.timeout)
util.delete_volume()
util.volume_deleted(args.timeout)
if util.msgs:
utils.critical(", ".join(util.msgs))
duration = util.get_duration()
notification = ""
if util.notifications:
notification = "(" + ", ".join(util.notifications) + ")"
utils.ok("Volume spawned and deleted in %d seconds %s| time=%d"
% (duration, notification, duration))
def check_cinder_volume():
utils.safe_run(_check_cinder_volume)

View File

@ -1,130 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 oschecks import utils
def _check_glance_api():
glance = utils.Glance()
glance.add_argument('-w', dest='warning', type=int, default=5,
help='Warning timeout for Glance APIs calls')
glance.add_argument('-c', dest='critical', type=int, default=10,
help='Critical timeout for Glance APIs calls')
options, args, client = glance.setup()
def images_list():
return list(client.images.list())
elapsed, images = utils.timeit(images_list)
if not images:
utils.critical("Unable to contact Glance API.")
if elapsed > options.critical:
utils.critical("Get images took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.critical, elapsed))
elif elapsed > options.warning:
utils.warning("Get images took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.warning, elapsed))
else:
utils.ok("Get images, Glance API is working: "
"list %d images in %d seconds.|response_time=%d" %
(len(images), elapsed, elapsed))
def check_glance_api():
utils.safe_run(_check_glance_api)
def _check_glance_image_exists():
glance = utils.Glance()
glance.add_argument('--req_count', dest='req_count', type=int,
required=False, default=0,
help='minimum number of images in glance')
glance.add_argument('--req_images', metavar='req_images', type=str,
nargs='+', required=False,
help='name of images who must be available')
options, args, client = glance.setup()
# Flags resultat
valid_image = 0
count = len(list(client.images.list(**{"limit": options.req_count or 1})))
if options.req_images:
required_images = options.req_images
for image in required_images:
try:
if len(list(client.images.list(
**{"filters": {"name": image}}))) == 1:
valid_image = valid_image + 1
except Exception:
pass
if options.req_count and count < options.req_count:
utils.critical("Failed - less than %d images found (%d)" %
(options.req_count, count))
if options.req_images and valid_image < len(required_images):
utils.critical("Failed - '%s' %d/%d images found " %
(", ".join(required_images), valid_image,
len(required_images)))
if options.req_images and options.req_count:
utils.ok("image %s found and enough images >=%d" %
(", ".join(required_images), options.req_count))
elif options.req_images:
utils.ok("image %s found" % (", ".join(required_images)))
elif options.req_count:
utils.ok("more than %d images found" % (count))
else:
utils.ok("Connection glance established")
def check_glance_image_exists():
utils.safe_run(_check_glance_image_exists)
def _check_glance_upload():
glance = utils.Glance()
glance.add_argument('--monitoring-image', dest='image_name', type=str,
default="openstack-monitoring-test-image",
help='Name of the monitoring image')
options, args, client = glance.setup()
data_raw = "X" * 1024 * 1024
elapsed, res = utils.timeit(client.images.create,
data=data_raw,
disk_format='raw',
container_format='bare',
name=options.image_name)
if not res or not res.id or res.status != 'active':
utils.critical("Unable to upload image in Glance")
res.delete()
if elapsed > 20:
utils.warning("Upload image in 20 seconds, it's too long")
else:
utils.ok("Glance image uploaded in %s seconds" % elapsed)
def check_glance_upload():
utils.safe_run(_check_glance_upload)

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 oschecks import utils
def _check_keystone_api():
keystone = utils.Keystone()
def check_token():
options, args, client = keystone.setup()
return client.service_catalog.get_token()
elapsed, token = utils.timeit(check_token)
if not token:
utils.critical("Unable to get a token")
if elapsed > 10:
utils.warning("Got a token after 10 seconds, it's too long."
"|response_time=%s" % elapsed)
else:
utils.ok("Got a token, Keystone API is working."
"|response_time=%s" % elapsed)
def check_keystone_api():
utils.safe_run(_check_keystone_api)

View File

@ -1,307 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Authors: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
# Sofer Athlan-Guyot <sofer.athlan@enovance.com>
#
# 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 argparse
import datetime
import logging
import os
import re
import urlparse
from keystoneclient.v2_0 import client
from neutronclient.neutron import client as neutron
from oschecks import utils
def _check_neutron_api():
neutron = utils.Neutron()
neutron.add_argument('-w', dest='warning', type=int, default=5,
help='Warning timeout for neutron APIs calls')
neutron.add_argument('-c', dest='critical', type=int, default=10,
help='Critical timeout for neutron APIs calls')
options, args, client = neutron.setup()
elapsed, networks = utils.timeit(client.list_networks)
if not networks or len(networks.get('networks', [])) <= 0:
utils.critical("Unable to contact neutron API.")
if elapsed > options.critical:
utils.critical("Get networks took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.critical, elapsed))
elif elapsed > options.warning:
utils.warning("Get networks took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.warning, elapsed))
else:
utils.ok("Get networks, neutron API is working: "
"list %d networks in %d seconds.|response_time=%d" %
(len(networks['networks']), elapsed, elapsed))
def check_neutron_api():
utils.safe_run(_check_neutron_api)
DAEMON_DEFAULT_PORT = 9696
def mangle_url(orig_url, url):
try:
endpoint_url = urlparse.urlparse(url)
except Exception as e:
utils.unknown("you must provide an endpoint_url in the form"
+ "<scheme>://<url>/ (%s)\n" % e)
scheme = endpoint_url.scheme
if scheme is None:
utils.unknown("you must provide an endpoint_url in the form"
+ "<scheme>://<url>/ (%s)\n" % e)
catalog_url = urlparse.urlparse(orig_url)
port = endpoint_url.port
if port is None:
if catalog_url.port is None:
port = DAEMON_DEFAULT_PORT
else:
port = catalog_url.port
netloc = "%s:%i" % (endpoint_url.hostname, port)
url = urlparse.urlunparse([scheme,
netloc,
catalog_url.path,
catalog_url.params,
catalog_url.query,
catalog_url.fragment])
return url
class Novautils(object):
def __init__(self, nova_client, tenant_id):
self.nova_client = nova_client
self.msgs = []
self.start = self.totimestamp()
self.notifications = ["floatingip_creation_time=%s" % self.start]
self.connection_done = False
self.all_floating_ips = []
self.fip = None
self.network_id = None
self.tenant_id = tenant_id
# python has no "toepoch" method: http://bugs.python.org/issue2736
# now, after checking http://stackoverflow.com/a/16307378,
# and http://stackoverflow.com/a/8778548 made my mind to this approach
@staticmethod
def totimestamp(dt=None, epoch=datetime.datetime(1970, 1, 1)):
if not dt:
dt = datetime.datetime.utcnow()
td = dt - epoch
# return td.total_seconds()
return int((td.microseconds + (td.seconds + td.days * 24 * 3600)
* 10**6) / 1e6)
def check_connection(self, force=False):
if not self.connection_done or force:
try:
# force a connection to the server
self.connection_done = self.nova_client.list_ports()
except Exception as e:
utils.critical("Cannot connect to neutron: %s\n" % e)
def get_duration(self):
return self.totimestamp() - self.start
def list_floating_ips(self):
if not self.all_floating_ips:
for floating_ip in self.nova_client.list_floatingips(
fields=['floating_ip_address', 'id'],
tenant_id=self.tenant_id)['floatingips']:
self.all_floating_ips.append(floating_ip)
return self.all_floating_ips
def check_existing_floatingip(self, floating_ip=None, delete=False):
count = 0
found_ips = []
for ip in self.list_floating_ips():
if floating_ip == 'all' or floating_ip.match(
ip['floating_ip_address']):
if delete:
# asynchronous call, we do not check that it worked
self.nova_client.delete_floatingip(ip['id'])
found_ips.append(ip['floating_ip_address'])
count += 1
if count > 0:
if delete:
self.notifications.append("Found %d ip(s): %s"
% (count, '{' + ', '.join(
found_ips) + '}'))
else:
self.msgs.append("Found %d ip(s): %s. "
% (count, ', '.join(found_ips))
+ "Won't create test floating ip. "
+ "Please check and delete.")
def get_network_id(self, router_name):
if not self.msgs:
if not self.network_id:
try:
self.network_id = self.nova_client.list_networks(
name=router_name, fields='id')['networks'][0]['id']
except Exception:
self.msgs.append("Cannot find ext router named '%s'."
% router_name)
def create_floating_ip(self):
if not self.msgs:
try:
body = {'floatingip': {'floating_network_id': self.network_id}}
self.fip = self.nova_client.create_floatingip(body=body)
self.notifications.append(
"fip=%s" % self.fip['floatingip']['floating_ip_address'])
except Exception as e:
self.msgs.append("Cannot create a floating ip: %s" % e)
def delete_floating_ip(self):
if not self.msgs:
try:
self.nova_client.delete_floatingip(
self.fip['floatingip']['id'])
except Exception:
self.msgs.append("Cannot remove floating ip %s"
% self.fip['floatingip']['id'])
def fip_type(string):
if string == 'all':
return 'all'
else:
return re.compile(string)
def _check_neutron_floating_ip():
parser = argparse.ArgumentParser(
description='Check an Floating ip creation. Note that it is able '
+ 'to delete *all* floating ips from a account, so '
+ 'ensure that nothing important is running on the '
+ 'specified account.')
parser.add_argument('--auth_url', metavar='URL', type=str,
default=os.getenv('OS_AUTH_URL'),
help='Keystone URL')
parser.add_argument('--username', metavar='username', type=str,
default=os.getenv('OS_USERNAME'),
help='username to use for authentication')
parser.add_argument('--password', metavar='password', type=str,
default=os.getenv('OS_PASSWORD'),
help='password to use for authentication')
parser.add_argument('--tenant', metavar='tenant', type=str,
default=os.getenv('OS_TENANT_NAME'),
help='tenant name to use for authentication')
parser.add_argument('--endpoint_url', metavar='endpoint_url', type=str,
help='Override the catalog endpoint.')
parser.add_argument('--endpoint_type', metavar='endpoint_type', type=str,
default="publicURL",
help='Endpoint type in the catalog request. '
+ 'Public by default.')
parser.add_argument('--force_delete', action='store_true',
help='If matching floating ip are found, delete them '
+ 'and add a notification in the message instead of '
+ 'getting out in critical state.')
parser.add_argument('--timeout', metavar='timeout', type=int,
default=120,
help='Max number of second to create/delete a '
+ 'floating ip (120 by default).')
parser.add_argument('--floating_ip', metavar='floating_ip', type=fip_type,
default=None,
help='Regex of IP(s) to check for existance. '
+ 'This value can be "all" for conveniance (match '
+ 'all ip). This permit to avoid certain floating '
+ 'ip to be kept. Its default value prevents the '
+ 'removal of any existing floating ip')
parser.add_argument('--ext_router_name', metavar='ext_router_name',
type=str, default='public',
help='Name of the "public" router (public by default)')
parser.add_argument('--verbose', action='count',
help='Print requests on stderr.')
args = parser.parse_args()
# this shouldn't raise any exception as no connection is done when
# creating the object. But It may change, so I catch everything.
try:
nova_client = client.Client(
username=args.username,
tenant_name=args.tenant,
password=args.password,
auth_url=args.auth_url,
)
nova_client.authenticate()
except Exception as e:
utils.critical("Authentication error: %s\n" % e)
try:
endpoint = nova_client.service_catalog.get_endpoints(
'network')['network'][0][args.endpoint_type]
if args.endpoint_url:
endpoint = mangle_url(endpoint, args.endpoint_url)
token = nova_client.service_catalog.get_token()['id']
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
neutron_client = neutron.Client('2.0', endpoint_url=endpoint,
token=token)
except Exception as e:
utils.critical("Error creating neutron object: %s\n" % e)
util = Novautils(neutron_client, nova_client.tenant_id)
# Initiate the first connection and catch error.
util.check_connection()
if args.floating_ip:
util.check_existing_floatingip(args.floating_ip, args.force_delete)
util.get_network_id(args.ext_router_name)
util.create_floating_ip()
util.delete_floating_ip()
if util.msgs:
utils.critical(", ".join(util.msgs))
duration = util.get_duration()
notification = ""
if util.notifications:
notification = "(" + ", ".join(util.notifications) + ")"
utils.ok("Floating ip created and deleted %s| time=%d"
% (notification, duration))
def check_neutron_floating_ip():
utils.safe_run(_check_neutron_floating_ip)

View File

@ -1,396 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2012-2014 eNovance <licensing@enovance.com>
#
# Authors: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
# Sofer Athlan-Guyot <sofer.athlan@enovance.com>
#
# 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 argparse
import datetime
import logging
import os
import time
from novaclient.client import Client # noqa
from novaclient import exceptions
from six.moves import urllib
from oschecks import utils
def _check_nova_api():
nova = utils.Nova()
nova.add_argument('-w', dest='warning', type=int, default=5,
help='Warning timeout for nova APIs calls')
nova.add_argument('-c', dest='critical', type=int, default=10,
help='Critical timeout for nova APIs calls')
options, args, client = nova.setup()
def flavors_list():
return list(client.flavors.list())
elapsed, flavors = utils.timeit(flavors_list)
if not flavors:
utils.critical("Unable to contact nova API.")
if elapsed > options.critical:
utils.critical("Get flavors took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.critical, elapsed))
elif elapsed > options.warning:
utils.warning("Get flavors took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.warning, elapsed))
else:
utils.ok("Get flavors, nova API is working: "
"list %d flavors in %d seconds.|response_time=%d" %
(len(flavors), elapsed, elapsed))
def check_nova_api():
utils.safe_run(_check_nova_api)
default_image_name = 'cirros'
default_flavor_name = 'm1.tiny'
default_instance_name = 'monitoring_test'
class Novautils(object):
def __init__(self, nova_client):
self.nova_client = nova_client
self.msgs = []
self.start = self.totimestamp()
self.notifications = ["instance_creation_time=%s" % self.start]
self.performances = []
self.instance = None
self.connection_done = False
# python has no "toepoch" method: http://bugs.python.org/issue2736
# now, after checking http://stackoverflow.com/a/16307378,
# and http://stackoverflow.com/a/8778548 made my mind to this approach
@staticmethod
def totimestamp(dt=None, epoch=datetime.datetime(1970, 1, 1)):
if not dt:
dt = datetime.datetime.utcnow()
td = dt - epoch
# return td.total_seconds()
return int((td.microseconds + (td.seconds + td.days * 24 * 3600)
* 10**6) / 1e6)
def check_connection(self, force=False):
if not self.connection_done or force:
try:
# force a connection to the server
self.connection_done = self.nova_client.limits.get()
except Exception as e:
utils.critical("Cannot connect to nova: %s\n" % e)
def get_duration(self):
return self.totimestamp() - self.start
def mangle_url(self, url):
self.check_connection()
try:
endpoint_url = urllib.parse.urlparse(url)
except Exception as e:
utils.unknown("you must provide an endpoint_url in the form"
+ "<scheme>://<url>/ (%s)\n" % e)
scheme = endpoint_url.scheme
if scheme is None:
utils.unknown("you must provide an endpoint_url in the form"
+ "<scheme>://<url>/ (%s)\n" % e)
catalog_url = None
try:
catalog_url = urllib.parse.urlparse(
self.nova_client.client.management_url)
except Exception as e:
utils.unknown("unknown error parsing the catalog url : %s\n" % e)
port = endpoint_url.port
if port is None:
if catalog_url.port is None:
port = 8774
else:
port = catalog_url.port
netloc = "%s:%i" % (endpoint_url.hostname, port)
url = urllib.parse.urlunparse([scheme,
netloc,
catalog_url.path,
catalog_url.params,
catalog_url.query,
catalog_url.fragment])
self.nova_client.client.set_management_url(url)
def check_existing_instance(self, instance_name, delete, timeout=45):
count = 0
for s in self.nova_client.servers.list():
if s.name == instance_name:
if delete:
s.delete()
self._instance_status(s, timeout, count)
self.performances.append("undeleted_server_%s_%d=%s"
% (s.name, count, s.created))
count += 1
if count > 0:
if delete:
self.notifications.append("Found '%s' present %d time(s)"
% (instance_name, count))
else:
self.msgs.append(
"Found '%s' present %d time(s). " % (instance_name, count)
+ "Won't create test instance. "
+ "Please check and delete.")
def get_image(self, image_name):
if not self.msgs:
try:
self.image = self.nova_client.images.find(name=image_name)
except Exception as e:
self.msgs.append("Cannot find the image %s (%s)"
% (image_name, e))
def get_flavor(self, flavor_name):
if not self.msgs:
try:
self.flavor = self.nova_client.flavors.find(name=flavor_name)
except Exception as e:
self.msgs.append("Cannot find the flavor %s (%s)"
% (flavor_name, e))
def create_instance(self, instance_name, network):
if not self.msgs:
kwargs = {}
try:
if network:
try:
network = self.nova_client.networks.find(
label=network).id
except exceptions.NotFound:
try:
network = self.nova_client.networks.find(
id=network).id
except exceptions.NotFound:
self.msgs.append("Cannot found network %s" %
network)
return
kwargs['nics'] = [{'net-id': network}]
self.instance = self.nova_client.servers.create(
name=instance_name,
image=self.image,
flavor=self.flavor, **kwargs)
except Exception as e:
self.msgs.append("Cannot create the vm %s (%s)"
% (instance_name, e))
def instance_ready(self, timeout):
if not self.msgs:
timer = 0
while self.instance.status != "ACTIVE":
if timer >= timeout:
self.msgs.append("Cannot create the vm")
break
time.sleep(1)
timer += 1
try:
self.instance.get()
except Exception as e:
self.msgs.append("Problem getting the status of the vm: %s"
% e)
break
def delete_instance(self):
if not self.msgs or self.instance is not None:
try:
self.instance.delete()
except Exception as e:
self.msgs.append("Problem deleting the vm: %s" % e)
def instance_deleted(self, timeout):
deleted = False
timer = 0
while not deleted and not self.msgs:
time.sleep(1)
if timer >= timeout:
self.msgs.append("Could not delete the vm within %d seconds"
% timer)
break
timer += 1
try:
self.instance.get()
except exceptions.NotFound:
deleted = True
except Exception as e:
self.msgs.append("Cannot delete the vm (%s)" % e)
break
def _instance_status(self, instance, timeout, count):
deleted = False
timer = 0
while not deleted:
time.sleep(1)
if timer >= timeout:
self.msgs.append(
"Could not delete the vm %s within %d seconds "
% (instance.name, timer)
+ "(created at %s)"
% instance.created)
break
timer += 1
try:
instance.get()
except exceptions.NotFound:
deleted = True
except Exception as e:
self.msgs.append("Cannot delete the vm %s (%s)"
% (instance.name, e))
self.performances.append("undeleted_server_%s_%d=%s"
% (instance.name,
count,
instance.created))
break
def _check_nova_instance():
parser = argparse.ArgumentParser(
description='Check an OpenStack Keystone server.')
parser.add_argument('--auth_url', metavar='URL', type=str,
default=os.getenv('OS_AUTH_URL'),
help='Keystone URL')
parser.add_argument('--username', metavar='username', type=str,
default=os.getenv('OS_USERNAME'),
help='username to use for authentication')
parser.add_argument('--password', metavar='password', type=str,
default=os.getenv('OS_PASSWORD'),
help='password to use for authentication')
parser.add_argument('--tenant', metavar='tenant', type=str,
default=os.getenv('OS_TENANT_NAME'),
help='tenant name to use for authentication')
parser.add_argument('--endpoint_url', metavar='endpoint_url', type=str,
help='Override the catalog endpoint.')
parser.add_argument('--endpoint_type', metavar='endpoint_type', type=str,
default="publicURL",
help='Endpoint type in the catalog request.'
+ 'Public by default.')
parser.add_argument('--image_name', metavar='image_name', type=str,
default=default_image_name,
help="Image name to use (%s by default)"
% default_image_name)
parser.add_argument('--flavor_name', metavar='flavor_name', type=str,
default=default_flavor_name,
help="Flavor name to use (%s by default)"
% default_flavor_name)
parser.add_argument('--instance_name', metavar='instance_name', type=str,
default=default_instance_name,
help="Instance name to use (%s by default)"
% default_instance_name)
parser.add_argument('--force_delete', action='store_true',
help='If matching instances are found delete them and '
+ 'add a notification in the message instead of '
+ 'getting out in critical state.')
parser.add_argument('--api_version', metavar='api_version', type=str,
default='2',
help='Version of the API to use. 2 by default.')
parser.add_argument('--timeout', metavar='timeout', type=int,
default=120,
help='Max number of second to create a instance'
+ '(120 by default)')
parser.add_argument('--timeout_delete', metavar='timeout_delete', type=int,
default=45,
help='Max number of second to delete an existing '
+ 'instance (45 by default).')
parser.add_argument('--insecure', action='store_true',
help="The server's cert will not be verified")
parser.add_argument('--network', metavar='network', type=str,
help="Override the network name or ID to use")
parser.add_argument('--verbose', action='count',
help='Print requests on stderr.')
args = parser.parse_args()
# this shouldn't raise any exception as no connection is done when
# creating the object. But It may change, so I catch everything.
try:
nova_client = Client(args.api_version,
username=args.username,
project_id=args.tenant,
api_key=args.password,
auth_url=args.auth_url,
endpoint_type=args.endpoint_type,
http_log_debug=args.verbose,
insecure=args.insecure)
except Exception as e:
utils.critical("Error creating nova communication object: %s\n" % e)
util = Novautils(nova_client)
if args.verbose:
ch = logging.StreamHandler()
nova_client.client._logger.setLevel(logging.DEBUG)
nova_client.client._logger.addHandler(ch)
# Initiate the first connection and catch error.
util.check_connection()
if args.endpoint_url:
util.mangle_url(args.endpoint_url)
# after mangling the url, the endpoint has changed. Check that
# it's valid.
util.check_connection(force=True)
util.check_existing_instance(args.instance_name,
args.force_delete,
args.timeout_delete)
util.get_image(args.image_name)
util.get_flavor(args.flavor_name)
util.create_instance(args.instance_name, args.network)
util.instance_ready(args.timeout)
util.delete_instance()
util.instance_deleted(args.timeout)
if util.msgs:
utils.critical(", ".join(util.msgs))
duration = util.get_duration()
notification = ""
if util.notifications:
notification = "(" + ", ".join(util.notifications) + ")"
performance = ""
if util.performances:
performance = " ".join(util.performances)
utils.ok("Nova instance spawned and deleted in %d seconds %s| time=%d %s"
% (duration, notification, duration, performance))
def check_nova_instance():
utils.safe_run(_check_nova_instance)

View File

@ -1,77 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author:Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 argparse
import os
import shlex
import subprocess
try:
import utils
except ImportError:
from oschecks import utils
def _pacemaker_host_check():
parser = argparse.ArgumentParser(
description='Check amqp connection of an OpenStack service.')
parser.add_argument('-r', dest='pacemaker_resource',
help='pacemaker resource', required=True)
parser.add_argument('-s', dest='script', required=True,
help='Script')
options = parser.parse_args()
local_hostname = subprocess.check_output(['hostname', '-s']).strip()
try:
output = subprocess.check_output(['pcs', 'status'])
except subprocess.CalledProcessError as e:
utils.critical('pcs status with status %s: %s' %
e.returncode, e.output)
except OSError:
utils.critical('pcs not found')
for line in output.splitlines():
line = " ".join(line.strip().split()) # Sanitize separator
if not line:
continue
resource, remaining = line.split(None, 1)
if resource == options.pacemaker_resource:
agent, __, remaining = remaining.partition(' ')
if ' ' in remaining:
status, __, current_hostname = remaining.partition(' ')
else:
status, current_hostname = remaining, ''
if status != "Started":
utils.critical("pacemaker resource %s is not started (%s)" %
(resource, status))
if current_hostname != local_hostname:
utils.ok("pacemaker resource %s doesn't on this node "
"(but on %s)" % (resource, current_hostname))
script = shlex.split(options.script)
os.execvp(script[0], script)
else:
utils.critical('pacemaker resource %s not found' %
options.pacemaker_resource)
def pacemaker_host_check():
utils.safe_run(_pacemaker_host_check)

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# 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 argparse
from oschecks import utils
def check_process():
parser = argparse.ArgumentParser(
description='Check process existence of an OpenStack service.')
parser.add_argument(dest='process_name',
help='Process name')
options = parser.parse_args()
utils.check_process_exists(options.process_name)
def main():
utils.safe_run(check_process)

View File

@ -1,44 +0,0 @@
#
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
#
# Author: Frederic Lepied <frederic.lepied@enovance.com>
#
# 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 unittest
from oschecks import ceph
class TestCephHealth(unittest.TestCase):
def test_interpret_output_ok(self):
exit_code, message = ceph.interpret_output_health('HEALTH_OK message')
self.assertEqual(exit_code, 0)
self.assertEqual(message, 'CEPH OK: message')
def test_interpret_output_warn(self):
exit_code, message = ceph.interpret_output_health('HEALTH_WARN '
'message')
self.assertEqual(exit_code, 1)
self.assertEqual(message, 'CEPH WARNING: message')
def test_interpret_output_critical(self):
exit_code, message = ceph.interpret_output_health('HEALTH_ERR message')
self.assertEqual(exit_code, 2)
self.assertEqual(message, 'CEPH CRITICAL: message')
def test_interpret_output_unknown(self):
exit_code, message = ceph.interpret_output_health('strange message')
self.assertEqual(exit_code, 3)
self.assertEqual(message, 'CEPH UNKNOWN: strange message')

View File

@ -1,45 +0,0 @@
#
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
#
# Author: Julien Danjou <julien@danjou.info>
#
# 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 subprocess
import unittest
class TestScripts(unittest.TestCase):
SCRIPTS = [
'amqp',
'ceilometer_api',
'ceph_df',
'ceph_health',
'cinder_api',
'cinder_volume',
'glance_api',
'glance_image_exists',
'glance_upload',
'keystone_api',
'neutron_api',
'neutron_floating_ip',
'nova_api',
'nova_instance',
]
for script in TestScripts.SCRIPTS:
def test_check_script(self):
proc = subprocess.Popen("oschecks-check_" + script)
proc.wait()
self.assertEqual(2, proc.returncode)
setattr(TestScripts, "test_script_check_" + script, test_check_script)

View File

@ -1,276 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Openstack Monitoring script for Sensu / Nagios
#
# Copyright © 2013-2014 eNovance <licensing@enovance.com>
#
# Author: Mehdi Abaakouk <mehdi.abaakouk@enovance.com>
#
# 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 copy
import itertools
import os
import sys
import time
import traceback
import psutil
AMQP_PORT = 5672
def unknown(msg):
print("UNKNOWN: %s" % msg)
sys.exit(3)
def critical(msg):
print("CRITICAL: %s" % msg)
sys.exit(2)
def warning(msg):
print("WARNING: %s" % msg)
sys.exit(1)
def ok(msg):
print("OK: %s" % msg)
sys.exit(0)
def check_process_name(name, p):
if p.name == name:
return True
# name can be truncated and a script so check also if it can be an
# argument to an interpreter
if len(p.cmdline) > 0 and os.path.basename(p.cmdline[0]) == name:
return True
if len(p.cmdline) > 1 and os.path.basename(p.cmdline[1]) == name:
return True
return False
def check_process_exists_and_amqp_connected(name):
processes = filter(lambda p: check_process_name(name, p),
psutil.process_iter())
if not processes:
critical("%s is not running" % name)
for p in processes:
try:
connections = p.get_connections(kind='inet')
except psutil.NoSuchProcess:
continue
found_amqp = (
len(list(itertools.takewhile(lambda c: len(c.remote_address) <= 1
or c.remote_address[1]
!= AMQP_PORT, connections)))
!= len(connections))
if found_amqp:
ok("%s is working." % name)
critical("%s is not connected to AMQP" % name)
def check_process_exists(name):
processes = filter(lambda p: check_process_name(name, p),
psutil.process_iter())
if not processes:
critical("%s is not running" % name)
ok("%s is working." % name)
def timeit_wrapper(func):
def wrapper(*arg, **kw):
t1 = time.time()
res = func(*arg, **kw)
t2 = time.time()
return (t2 - t1), res
return wrapper
@timeit_wrapper
def timeit(func, *args, **kwargs):
return func(*args, **kwargs)
def safe_run(method):
try:
method()
except Exception:
critical(traceback.format_exc())
class Nova(object):
def __init__(self):
from novaclient import shell
self.nova = shell.OpenStackComputeShell()
self.base_argv = copy.deepcopy(sys.argv[1:])
self.nova.parser = self.nova.get_base_parser()
self.add_argument = self.nova.parser.add_argument
def setup(self, api_version='1.1'):
from novaclient import client
(options, args) = self.nova.parser.parse_known_args(self.base_argv)
if options.help:
options.command = None
self.nova.do_help(options)
sys.exit(2)
auth_token = None
if options.os_auth_token and options.os_endpoint:
auth_token = options.os_auth_token
if options.os_compute_api_version:
api_version = options.os_compute_api_version
client = client.get_client_class(api_version)(
options.os_username,
options.os_password,
options.os_tenant_name,
tenant_id=options.os_tenant_id,
auth_token=auth_token,
auth_url=options.os_auth_url,
region_name=options.os_region_name,
cacert=options.os_cacert,
insecure=options.insecure,
timeout=options.timeout)
return options, args, client
class Glance(object):
def __init__(self):
from glanceclient import shell
self.glance = shell.OpenStackImagesShell()
self.base_argv = copy.deepcopy(sys.argv[1:])
self.glance.parser = self.glance.get_base_parser()
self.add_argument = self.glance.parser.add_argument
def setup(self, api_version=1):
(options, args) = self.glance.parser.parse_known_args(self.base_argv)
if options.help:
options.command = None
self.glance.do_help(options)
sys.exit(2)
client = self.glance._get_versioned_client(api_version, options,
force_auth=True)
return options, args, client
class Ceilometer(object):
def __init__(self):
from ceilometerclient import shell
self.ceilometer = shell.CeilometerShell()
self.base_argv = copy.deepcopy(sys.argv[1:])
# NOTE(gordc): workaround for bug1434264
if not hasattr(self.ceilometer, 'auth_plugin'):
from ceilometerclient import client
if hasattr(client, 'AuthPlugin'):
self.ceilometer.auth_plugin = client.AuthPlugin()
self.ceilometer.parser = self.ceilometer.get_base_parser()
self.add_argument = self.ceilometer.parser.add_argument
def setup(self, api_version=2):
from ceilometerclient import client
(options, args) = self.ceilometer.parser.parse_known_args(
self.base_argv)
if options.help:
options.command = None
self.do_help(options)
sys.exit(2)
client_kwargs = vars(options)
return options, client.get_client(api_version, **client_kwargs)
class Cinder(object):
def __init__(self):
from cinderclient import shell
self.cinder = shell.OpenStackCinderShell()
self.base_argv = copy.deepcopy(sys.argv[1:])
self.cinder.parser = self.cinder.get_base_parser()
self.add_argument = self.cinder.parser.add_argument
def setup(self, api_version='1'):
from cinderclient import client
(options, args) = self.cinder.parser.parse_known_args(self.base_argv)
if options.help:
options.command = None
self.cinder.do_help(options)
sys.exit(2)
if options.os_volume_api_version:
api_version = options.os_volume_api_version
client = client.get_client_class(api_version)(
options.os_username,
options.os_password,
options.os_tenant_name,
tenant_id=options.os_tenant_id,
auth_url=options.os_auth_url,
region_name=options.os_region_name,
cacert=options.os_cacert,
insecure=options.insecure)
return options, args, client
class Neutron(object):
def __init__(self):
from neutronclient import shell
self.neutron = shell.NeutronShell('2.0')
self.base_argv = copy.deepcopy(sys.argv[1:])
self.neutron.parser = self.neutron.build_option_parser(
"Neutron client", "2.0")
self.add_argument = self.neutron.parser.add_argument
def setup(self):
(options, args) = self.neutron.parser.parse_known_args(self.base_argv)
self.neutron.options = options
self.neutron.api_version = {'network': self.neutron.api_version}
self.neutron.authenticate_user()
return options, args, self.neutron.client_manager.neutron
class Keystone(object):
def __init__(self):
from keystoneclient import shell
self.keystone = shell.OpenStackIdentityShell()
self.base_argv = copy.deepcopy(sys.argv[1:])
self.keystone.parser = self.keystone.get_base_parser()
self.add_argument = self.keystone.parser.add_argument
def setup(self, api_version="2.0"):
(options, args) = self.keystone.parser.parse_known_args(self.base_argv)
if options.help:
self.keystone.do_help(options)
sys.exit(2)
self.keystone.auth_check(options)
token = None
if options.os_token and options.os_endpoint:
token = options.os_token
if options.os_identity_api_version:
api_version = options.os_identity_api_version
client = self.keystone.get_api_class(api_version)(
username=options.os_username,
tenant_name=options.os_tenant_name,
tenant_id=options.os_tenant_id,
token=token,
endpoint=options.os_endpoint,
password=options.os_password,
auth_url=options.os_auth_url,
region_name=options.os_region_name,
cacert=options.os_cacert,
key=options.os_key,
cert=options.os_cert,
insecure=options.insecure,
debug=options.debug,
use_keyring=options.os_cache,
force_new_token=options.force_new_token,
stale_duration=options.stale_duration,
timeout=options.timeout)
return options, args, client

View File

@ -1,8 +0,0 @@
psutil
python-ceilometerclient
python-cinderclient
python-glanceclient
python-keystoneclient
python-neutronclient
python-novaclient
six

View File

@ -1,60 +0,0 @@
[metadata]
name = monitoring-for-openstack
version = 1
summary = OpenStack monitoring tools
description-file =
README.rst
author = eNovance
author-email = techs@enovance.com
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 :: 2
Programming Language :: Python :: 2.6
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
Topic :: System :: Monitoring
[global]
setup-hooks =
pbr.hooks.setup_hook
[files]
packages =
oschecks
scripts =
legacy/oschecks-check_horizon_login
legacy/oschecks-check_swift
legacy/oschecks-check_swift_dispersion
legacy/oschecks-check_swift_object_servers
legacy/oschecks-check_swift_recon
legacy/oschecks-check_swift_ring_usage
[entry_points]
console_scripts =
oschecks-check_amqp = oschecks.amqp:main
oschecks-check_ceilometer_api = oschecks.ceilometer:check_ceilometer_api
oschecks-check_ceph_df = oschecks.ceph:check_ceph_df
oschecks-check_ceph_health = oschecks.ceph:check_ceph_health
oschecks-check_cinder_api = oschecks.cinder:check_cinder_api
oschecks-check_cinder_volume = oschecks.cinder:check_cinder_volume
oschecks-check_glance_api = oschecks.glance:check_glance_api
oschecks-check_glance_image_exists = oschecks.glance:check_glance_image_exists
oschecks-check_glance_upload = oschecks.glance:check_glance_upload
oschecks-check_keystone_api = oschecks.keystone:check_keystone_api
oschecks-check_neutron_api = oschecks.neutron:check_neutron_api
oschecks-check_neutron_floating_ip = oschecks.neutron:check_neutron_floating_ip
oschecks-check_nova_api = oschecks.nova:check_nova_api
oschecks-check_nova_instance = oschecks.nova:check_nova_instance
oschecks-pacemaker_host_check = oschecks.pacemaker_host_check:pacemaker_host_check
[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2014 eNovance
#
# 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 setuptools
setuptools.setup(
setup_requires=['pbr'],
pbr=True)

View File

@ -1,3 +0,0 @@
testrepository
discover
sphinx

22
tox.ini
View File

@ -1,22 +0,0 @@
[tox]
envlist = py26,py27,py33,py34,pep8
[testenv]
usedevelop = True
sitepackages = False
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
install_command = pip install -U {opts} {packages}
commands = python setup.py testr --slowest --testr-args="{posargs}"
[testenv:pep8]
deps = {[testenv]deps}
hacking>=0.9.2,<0.10
commands = flake8
[testenv:venv]
commands = {posargs}
[flake8]
exclude = .tox,doc
show-source = true