Retire repo and note new content in openstack/osops

Change-Id: I56f2e8ef904d9d844a65c2982b3b5f4fae38850e
Signed-off-by: Sean McGinnis <sean.mcginnis@gmail.com>
This commit is contained in:
Sean McGinnis 2020-09-10 20:05:01 -05:00
parent bafe3f0d2f
commit 1d5cf49317
No known key found for this signature in database
GPG Key ID: CE7EE4BFAF8D70C8
37 changed files with 10 additions and 3026 deletions

54
.gitignore vendored
View File

@ -1,54 +0,0 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
# Sphinx documentation
docs/_build/

201
LICENSE
View File

@ -1,201 +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:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) 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
(d) 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,24 +1,12 @@
# tools-monitoring
This project is no longer maintained. Its content has now moved to the
https://opendev.org/openstack/osops repo, and further development will
continue there.
Tools to monitor OpenStack
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".
Please see the wiki page at https://wiki.openstack.org/wiki/Osops for other osops tools & repos.
## Report bugs
Please use [launchpad][launchpad] to report bugs.
[launchpad]: https://bugs.launchpad.net/osops
## Directories
[monitoring-for-openstack/](monitoring-for-openstack): a python application to help do some monitoring for OpenStack Clouds
[sensu-plugins/](sensu-plugins): A collection of [sensu][sensu] specific plugins to help monitor OpenStack Clouds
## External Links
A collection of scripts to monitor various OpenStack components, for use with [Rackspace Cloud Monitoring (MaaS)][maas] can be found here:
[sensu]: https://sensuapp.org/
[maas]: https://github.com/rcbops/rpc-maas
For any further questions, please email
openstack-discuss@lists.openstack.org or join #openstack-dev on
Freenode.

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

View File

@ -1,25 +0,0 @@
========================
Monitoring for OpenStack
========================
This is a set of Python scripts to help with the monitoring of
OpenStack services.
Installation
============
* Clone the osops-tools-monitoring repository::
git clone https://github.com/openstack/osops-tools-monitoring
* Change to the ``monitoring-for-openstack`` directory::
cd osops-tools-monitoring/monitoring-for-openstack
* Install the requirements::
pip install -r requirements.txt
* Install the ``monitoring-for-openstack`` scripts::
python setup.py install

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,37 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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,63 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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 json
import re
from oschecks import utils
from ceilometerclient import exc
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()
def meters_list():
try:
return client.meters.list()
except exc.HTTPNotFound as ex:
utils.warning('Did not find Ceilometer API running '
'on given endpoint')
except Exception as ex:
utils.critical(str(ex))
elapsed, meters = utils.timeit(meters_list)
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,143 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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 remaining(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
if 'total_bytes' in data['stats']:
total = int(data['stats']['total_bytes'])
else:
total = int(data['stats']['total_space'])
if 'total_used_bytes' in data['stats']:
used = int(data['stats']['total_used_bytes'])
else:
used = int(data['stats']['total_used'])
if 'total_avail_bytes' in data['stats']:
avail = int(data['stats']['total_avail_bytes'])
else:
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" %
remaining(avail, total))
elif avail < per(warn_percent, total):
return (1, "[WARN] Ceph df avail. waring %s" %
remaining(avail, total))
else:
return (0, "[OK] Ceph df avail. seems good %s" %
remaining(avail, total))
def check_ceph_df():
'Program entry point.'
try:
ceph_args = ["ceph", "df", "--format=json"]
if len(sys.argv) >= 4:
ceph_args.append('-n')
ceph_args.append(sys.argv[3])
res = subprocess.check_output(ceph_args,
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:
ceph_args = ["ceph", "health"]
if len(sys.argv) >= 2:
ceph_args.append('-n')
ceph_args.append(sys.argv[1])
res = subprocess.check_output(ceph_args,
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,285 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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():
try:
return client.quotas.get(
getattr(options, 'os_project_name',
getattr(options, 'os_tenant_name', None)
)
)
except Exception as ex:
utils.critical(str(ex))
elapsed, quotas = utils.timeit(quotas_list)
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 CinderUtils(object):
DAEMON_DEFAULT_PORT = 8776
def __init__(self, client, project):
self.client = client
self.msgs = []
self.start = self.totimestamp()
self.notifications = ["volume_creation_time=%s" % self.start]
self.volume = None
self.connection_done = False
self.project = project
# 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.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.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.client.client.management_url = url
def check_existing_volume(self, volume_name, delete):
count = 0
for s in self.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 = {'name': volume_name, 'size': size}
if volume_type:
conf['volume_type'] = volume_type
if availability_zone:
conf['availability_zone'] = availability_zone
self.volume = self.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():
cinder = utils.Cinder()
cinder.add_argument('--endpoint_url', metavar='endpoint_url', type=str,
help='Override the catalog endpoint.')
cinder.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.')
cinder.add_argument('--volume_name', metavar='volume_name', type=str,
default="monitoring_test",
help='Name of the volume to create '
'(monitoring_test by default)')
cinder.add_argument('--volume_size', metavar='volume_size', type=int,
default=1,
help='Size of the volume to create (1 GB by default)')
cinder.add_argument('--volume_type', metavar='volume_type', type=str,
default=None,
help='With multiple backends, choose the volume type.')
cinder.add_argument('--availability_zone', metavar='availability_zone',
type=str,
default=None,
help='Specify availability zone.')
options, args, client = cinder.setup()
project = (options.os_project_id if options.os_project_id else
options.os_project_name)
tenant = (options.os_tenant_id if options.os_tenant_id else
options.os_tenant_name)
util = CinderUtils(client, tenant or project)
# Initiate the first connection and catch error.
util.check_connection()
if options.endpoint_url:
util.mangle_url(options.endpoint_url)
# after mangling the url, the endpoint has changed. Check that
# it's valid.
util.check_connection(force=True)
util.check_existing_volume(options.volume_name, options.force_delete)
util.create_volume(options.volume_name,
options.volume_size,
options.availability_zone,
options.volume_type)
util.volume_ready(options.timeout)
util.delete_volume()
util.volume_deleted(options.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,145 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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 StringIO
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():
try:
return list(client.images.list())
except Exception as ex:
utils.critical(str(ex))
elapsed, images = utils.timeit(images_list)
if images and elapsed > options.critical:
utils.critical("Get images took more than %d seconds, "
"it's too long.|response_time=%d" %
(options.critical, elapsed))
elif images and 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 _upload_image(client, name):
data = StringIO.StringIO("X" * 1024 * 1024)
img = client.images.create(name=name,
disk_format='raw',
container_format='bare')
try:
client.images.upload(img.id, data)
except Exception:
client.images.delete(img.id)
raise
return img.id
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()
elapsed, iid = utils.timeit(_upload_image,
client=client,
name=options.image_name)
try:
res = client.images.get(iid)
if res.status != 'active':
utils.critical("Unable to upload image in Glance")
finally:
client.images.delete(res.id)
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,48 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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():
try:
return keystone.run()
except Exception as ex:
utils.critical(str(ex))
elapsed, result = utils.timeit(check_token)
rc, out = result
if rc:
utils.critical("Unable to get a token:\n{0}".format(out))
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,270 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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()
def network_list():
try:
return client.list_networks()
except Exception as ex:
utils.critical(str(ex))
elapsed, networks = utils.timeit(network_list)
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)
class NeutronUtils(object):
DAEMON_DEFAULT_PORT = 9696
def __init__(self, client, tenant_id):
self.client = 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.client.list_ports()
except Exception as e:
utils.critical("Cannot connect to neutron: %s\n" % e)
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.client.httpclient.endpoint_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.client.httpclient.endpoint_override = url
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.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.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, ext_network_name):
if not self.msgs:
if not self.network_id:
try:
self.network_id = self.client.list_networks(
name=ext_network_name, fields='id')['networks'][0]['id']
except Exception:
self.msgs.append("Cannot find ext network named '%s'."
% ext_network_name)
def create_floating_ip(self):
if not self.msgs:
try:
body = {'floatingip': {'floating_network_id': self.network_id}}
self.fip = self.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.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():
neutron = utils.Neutron()
neutron.add_argument('--endpoint_url', metavar='endpoint_url', type=str,
help='Override the catalog endpoint.')
neutron.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.'))
neutron.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'))
neutron.add_argument('--ext_network_name', metavar='ext_network_name',
type=str, default='public',
help=('Name of the "public" external network '
'(public by default)'))
options, args, client = neutron.setup()
project = (options.os_project_id if options.os_project_id else
options.os_project_name)
tenant = (options.os_tenant_id if options.os_tenant_id else
options.os_tenant_name)
util = NeutronUtils(client, tenant or project)
# Initiate the first connection and catch error.
util.check_connection()
if options.endpoint_url:
util.mangle_url(options.endpoint_url)
# after mangling the url, the endpoint has changed. Check that
# it's valid.
util.check_connection(force=True)
if options.floating_ip:
util.check_existing_floatingip(options.floating_ip,
options.force_delete)
util.get_network_id(options.ext_network_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,362 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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():
try:
return list(client.flavors.list())
except Exception as ex:
utils.critical(str(ex))
elapsed, flavors = utils.timeit(flavors_list)
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:
if hasattr(self.nova_client, 'images'):
self.image = self.nova_client.images.find(name=image_name)
else:
self.image = self.nova_client.glance.find_image(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 get_network(self, network_name):
if self.msgs or not network_name:
self.network = None
elif hasattr(self.nova_client, 'networks'):
self.network = self.nova_client.networks.find(name=image_name)
else:
self.network = self.nova_client.neutron.find_network(image_name)
def create_instance(self, instance_name):
if not self.msgs:
kwargs = {}
try:
if self.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 find network %s" %
network)
return
kwargs['nics'] = [{'net-id': self.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():
nova = utils.Nova()
nova.add_argument('--endpoint_url', metavar='endpoint_url', type=str,
help='Override the catalog endpoint.')
nova.add_argument('--image_name', metavar='image_name', type=str,
default=default_image_name,
help="Image name to use (%s by default)"
% default_image_name)
nova.add_argument('--flavor_name', metavar='flavor_name', type=str,
default=default_flavor_name,
help="Flavor name to use (%s by default)"
% default_flavor_name)
nova.add_argument('--instance_name', metavar='instance_name', type=str,
default=default_instance_name,
help="Instance name to use (%s by default)"
% default_instance_name)
nova.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.')
nova.add_argument('--timeout_delete', metavar='timeout_delete',
type=int, default=45,
help='Max number of second to delete an existing '
'instance (45 by default).')
nova.add_argument('--network', metavar='network', type=str, default=None,
help="Override the network name to use")
nova.add_argument('--verbose', action='count',
help='Print requests on stderr.')
options, args, nova_client = nova.setup()
util = Novautils(nova_client)
if options.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 options.endpoint_url:
util.mangle_url(options.endpoint_url)
# after mangling the url, the endpoint has changed. Check that
# it's valid.
util.check_connection(force=True)
util.check_existing_instance(options.instance_name,
options.force_delete,
options.timeout_delete)
util.get_image(options.image_name)
util.get_flavor(options.flavor_name)
util.get_network(options.network)
util.create_instance(options.instance_name)
util.instance_ready(options.timeout)
util.delete_instance()
util.instance_deleted(options.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,161 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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
import re
try:
import utils
except ImportError:
from oschecks import utils
def _ok_run_script(options):
'''If there is a script to run it is executed otherwise a default message.
Argument:
options (Object) -- main program arguments
'''
if options.script:
script = shlex.split(options.script)
os.execvp(script[0], script)
else:
utils.ok("pacemaker resource %s is running"
% options.pacemaker_resource)
def _check_resource_in_host(remaining, match_word, options, local_hostname):
'''Searches for resource and a local_hostname on the rest of the line
It checks if the resource is the second or third word on the line and
search for the host on the running nodes
Arguments:
:param remaining: (str)-- the rest of the line
:param match_word: (str)-- 'Started:'-->Clone or 'Master'-->Master/Slave
:param options: (object)-- main program arguments
:param local_hostname: -- localhost
'''
engine = re.compile('Set: ('+options.pacemaker_resource+' \[.*\]|.* \['
+options.pacemaker_resource+'\]) '+match_word+' (\[.*?\])')
patterns = re.search(engine, remaining)
if patterns is not None:
host_list = patterns.group(2).split()[1:-1]
for host in host_list:
if host == local_hostname:
_ok_run_script(options)
utils.ok(
"pacemaker resource %s doesn't run on this node "
"(but on %s)" % (options.pacemaker_resource, patterns.group(2))
)
def _check_resource_in_docker_host(remaining, options, local_hostname):
'''Searches for Docker container set resources in an active state and
matches the local_hostname on the rest of the line
Arguments:
:param remaining: (str)-- the rest of the line
:param options: (object)-- main program arguments
:param local_hostname: -- localhost
'''
engine = re.compile('(container set: '+options.pacemaker_resource+''
' \[.*\].*)')
engine2 = re.compile('(?: Master | Slave | Started )(\S*)')
pattern = re.search(engine, remaining)
if pattern is not None:
sremaining = pattern.group(1).split('):')
for line in sremaining:
host = re.search(engine2, line)
if host is not None:
if host.group(1) == local_hostname:
_ok_run_script(options)
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=False,
help='Script')
parser.add_argument('--crm', dest='crm', required=False,
help='Use "crm_mon -1" instead of "pcs status"',
action='store_true', default=False)
options = parser.parse_args()
if options.script and (not os.path.isfile(options.script)
or not os.access(options.script, os.X_OK)):
utils.critical('the script %s could not be read' % options.script)
local_hostname = subprocess.check_output(['hostname', '-s']).strip()
try:
if options.crm :
p = subprocess.Popen(['crm_mon', '-1'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output, error = p.communicate()
else:
p = subprocess.Popen(['pcs', 'status'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output, error = p.communicate()
if p.returncode !=0:
if options.crm:
utils.critical('pcs status with status {}: {}'
.format(p.returncode, output.strip()))
else:
utils.critical('pcs status with status {}: {}'
.format(p.returncode, error.strip()))
except OSError:
utils.critical('pcs not found')
for line in re.sub("\n +", " ", 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 run on this node "
"(but on %s)" % (resource, current_hostname))
_ok_run_script(options)
elif resource == 'Clone' :
_check_resource_in_host(remaining, 'Started:', options,
local_hostname)
elif resource == 'Docker':
_check_resource_in_docker_host(remaining, options,
local_hostname)
elif resource == 'Master/Slave':
_check_resource_in_host(remaining, 'Masters:', options,
local_hostname)
else:
utils.critical('pacemaker resource %s not found' %
options.pacemaker_resource)
def pacemaker_host_check():
utils.safe_run(_pacemaker_host_check)

View File

@ -1,37 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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,45 +0,0 @@
# -*- coding: utf-8 -*-
#
# 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,46 +0,0 @@
# -*- coding: utf-8 -*-
#
# 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,306 +0,0 @@
#!/usr/bin/env python
# -*- coding: 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):
try:
len(p.cmdline)
except TypeError:
pname = p.name()
pcmdline = p.cmdline()
else:
pname = p.name
pcmdline = p.cmdline
if pname == name:
return True
# name can be truncated and a script so check also if it can be an
# argument to an interpreter
if len(pcmdline) > 0 and os.path.basename(pcmdline[0]) == name:
return True
if len(pcmdline) > 1 and os.path.basename(pcmdline[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:
conn_func = getattr(p, 'get_connections', p.connections)
connections = conn_func(kind='inet')
except psutil.NoSuchProcess:
continue
found_amqp = (
len(list(itertools.takewhile(lambda c:
len(getattr(c, 'remote_address', c.raddr)) <= 1 or
getattr(c, 'remote_address', c.raddr)[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.base_argv)
self.add_argument = self.nova.parser.add_argument
def setup(self, api_version='2.1'):
from novaclient.client 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 = getattr(args, 'os_token', None)
api_version = (
getattr(options, 'os_compute_api_version', api_version) or
api_version
)
try:
nova_client = Client(
api_version,
username=options.os_username,
password=options.os_password,
project_name=getattr(
options, 'os_project_name', getattr(
options, 'os_tenant_name', None
)
),
project_id=getattr(
options, 'os_project_id', getattr(
options, 'os_tenant_id', None
)
),
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)
except Exception as ex:
critical(ex)
return options, args, nova_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(sys.argv)
self.add_argument = self.glance.parser.add_argument
def setup(self, api_version=2):
(options, args) = self.glance.parser.parse_known_args(self.base_argv)
if options.help:
options.command = None
self.glance.do_help(options, self.glance.parser)
sys.exit(2)
api_version = (
getattr(options, 'os_image_api_version', api_version) or
api_version
)
try:
client = self.glance._get_versioned_client(api_version, options)
except Exception as ex:
critical(ex)
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:])
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.ceilometer.do_help(options)
sys.exit(2)
client_kwargs = vars(options)
try:
return options, client.get_client(api_version, **client_kwargs)
except Exception as ex:
critical(ex)
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
try:
client = client.get_client_class(api_version)(
options.os_username,
options.os_password,
project_id=getattr(
options, 'os_project_name', getattr(
options, 'os_tenant_name', None
)
),
auth_url=options.os_auth_url,
region_name=options.os_region_name,
cacert=options.os_cacert,
insecure=options.insecure)
except Exception as ex:
critical(ex)
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}
try:
self.neutron.authenticate_user()
return options, args, self.neutron.client_manager.neutron
except Exception as ex:
critical(ex)
class Keystone(object):
def __init__(self):
if (sys.version_info > (3, 0)):
# Python 3 code in this block
from io import StringIO
else:
# Python 2 code in this block
from StringIO import StringIO
from openstackclient import shell
self.shell = shell.OpenStackShell()
self.shell.stdout = StringIO()
self.shell.stderr = StringIO()
self.help = False
def run(self):
command = ['token', 'issue']
vformat = ['-f', 'value', '-c', 'id']
if 'help' in sys.argv or '--help' in sys.argv or '-h' in sys.argv:
rc = self.shell.run(command)
else:
cmd_arg = sys.argv[1:]
# removes parameters used in vformat
for opt in ['-f', '-c']:
if opt in cmd_arg:
index = cmd_arg.index(opt)
if len(cmd_arg) > (index + 1):
for i in range(2):
cmd_arg.pop(index)
rc = self.shell.run(command + cmd_arg + vformat)
out = self.shell.stdout.getvalue()
return rc, out

View File

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

View File

@ -1,52 +0,0 @@
[metadata]
name = monitoring-for-openstack
version = 1.0.1
summary = OpenStack monitoring tools
description-file = README.rst
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
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.7
Programming Language :: Python :: 3.5
Topic :: System :: Monitoring
[global]
setup-hooks =
pbr.hooks.setup_hook
[files]
packages =
oschecks
[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
[install]
install-scripts = /usr/libexec/openstack-monitoring/checks/
[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
python-subunit>=0.0.18
sphinx

View File

@ -1,40 +0,0 @@
[tox]
minversion = 1.6
skipsdist = True
envlist = py35,py27,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:bashate]
basepython = python3
deps = bashate
whitelist_externals = bash
commands = bash -c "find {toxinidir} \
-not \( -type d -name .?\* -prune \) \
-not \( -type d -name contrib -prune \) \
-type f \
-not -name \*~ \
-not -name \*.md \
-name \*.sh \
-print0 | xargs -0 bashate -v"
[testenv:pep8]
basepython = python3
deps = {[testenv]deps}
hacking>=0.9.2,<0.10
commands = flake8
[testenv:venv]
basepython = python3
commands = {posargs}
[flake8]
IGNORE= H105,H405
exclude = .tox,doc
show-source = true

View File

@ -1,87 +0,0 @@
#!/usr/bin/env python
# pylint: disable=import-error,too-few-public-methods
"""
Check ceilometer has recent data
"""
from ceilometerclient import client
import datetime
import argparse
import sys
import iso8601
import pytz
NAGIOS_OK = 0
NAGIOS_WARNING = 1
NAGIOS_CRITICAL = 2
NAGIOS_UNKNOWN = 3
class CeilometerConnect(object):
"""
Ceilometer connection class
"""
def __init__(self, args):
version = '2'
self.ceilo_connect = client.get_client(version,
os_username=args.username,
os_password=args.password,
os_tenant_name=args.tenant_name,
os_auth_url=args.auth_url)
def check_samples(self):
"""
Check meters are incrementing
"""
# Get last sample
sample = self.ceilo_connect.samples.list(meter_name='cpu', limit='1')
if sample:
return sample[0].recorded_at
else:
return False
def utcnow():
"""
Return timezone aware UTC date
"""
return datetime.datetime.now(tz=pytz.utc)
def get_args():
"""
Parse CLI arguments
"""
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--username', required=True)
parser.add_argument('-p', '--password', required=True)
parser.add_argument('-t', '--tenant_name', required=True)
parser.add_argument('-a', '--auth_url', required=True)
parser.add_argument('-w', '--warn', type=int, required=True)
parser.add_argument('-c', '--crit', type=int, required=True)
args = parser.parse_args()
return args
def main():
"""
Main script body
"""
args = get_args()
ceilo_connect = CeilometerConnect(args)
sample = ceilo_connect.check_samples()
if sample:
delta = utcnow() - iso8601.parse_date(sample)
if delta >= datetime.timedelta(minutes=args.crit):
print "CRITICAL: Ceilometer data behind by %s" % delta
sys.exit(NAGIOS_CRITICAL)
elif delta >= datetime.timedelta(minutes=args.warn):
print "WARNING: Ceilometer data behind by %s" % delta
sys.exit(NAGIOS_WARNING)
else:
print "OK: Ceilometer data updating - last updated at %s" % sample
sys.exit(NAGIOS_OK)
else:
print "UNKNOWN: Ceilometer data not returning"
sys.exit(NAGIOS_UNKNOWN)
if __name__ == "__main__":
main()

View File

@ -1,81 +0,0 @@
#!/usr/bin/env python
"""
Check for remaining IP addresses
"""
# pylint: disable=import-error
from neutronclient.v2_0 import client
from ipaddress import ip_network
import sys
import argparse
NAGIOS_OK = 0
NAGIOS_WARNING = 1
NAGIOS_CRITICAL = 2
NAGIOS_UNKNOWN = 3
def main():
"""
Main script body
"""
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--username', required=True)
parser.add_argument('-p', '--password', required=True)
parser.add_argument('-t', '--tenant_name', required=True)
parser.add_argument('-a', '--auth_url', required=True)
parser.add_argument('-w', '--warn', type=int, required=True)
parser.add_argument('-c', '--critical', type=int, required=True)
args = parser.parse_args()
neutron = client.Client(username=args.username, password=args.password,
tenant_name=args.tenant_name,
auth_url=args.auth_url)
neutron.format = 'json'
for arg in [args.warn, args.critical]:
if not 0 <= arg <= 100:
print "Alert parameters must be valid percentages"
sys.exit(NAGIOS_UNKNOWN)
# Get external network
# Assume a single external network for the minute
ext_net = [net for net in neutron.list_networks()['networks']
if net['router:external']]
total_addresses = 0
for subnet in neutron.show_network(ext_net[0]['id'])['network']['subnets']:
total_addresses += ip_network(neutron.show_subnet(subnet)
['subnet']['cidr']).num_addresses
floating_ips = len(neutron.list_floatingips()['floatingips'])
router_ips = len([router for router in neutron.list_routers()['routers']
if router['external_gateway_info']])
total_used = floating_ips + router_ips
percentage_used = 100 * total_used/total_addresses
if percentage_used >= args.warn:
code = NAGIOS_WARNING
msg = 'WARNING'
elif percentage_used >= args.critical:
code = NAGIOS_CRITICAL
msg = 'CRITICAL'
else:
code = NAGIOS_OK
msg = 'OK'
print '{0}: {1}% of IP pool used, '\
'{2} out of {5} addresses in use | '\
'total_used={2};{3};{4};;{5} '\
'total_available={5} '\
'floating_ips_used={6} '\
'ext_routers_used={7}'\
.format(msg, percentage_used, total_used,
(total_addresses * args.warn)/100,
(total_addresses * args.critical)/100,
total_addresses, floating_ips, router_ips)
sys.exit(code)
if __name__ == "__main__":
main()

View File

@ -1,16 +0,0 @@
#!/bin/bash
source ~/openrc
output=$(cinder service-list | grep down)
if [ $? -eq 0 ]; then
echo -n "CRITICAL - OpenStack Cinder services down: "
echo "${output}" | awk '{print $2,$4}' | while read LINE; do
echo -n "${LINE}; "
done
echo ""
exit 2
else
echo "OK - All nodes up"
exit 0
fi

View File

@ -1,19 +0,0 @@
#!/bin/bash
source ~/openrc
if [ -z "${1}" ]; then
image="CirrOS"
else
image="${1}"
fi
glance image-list | grep -q "${image}"
if [ $? == 0 ]; then
echo "OK - Glance is working."
exit 0
else
echo "CRITICAL - Glance is not working."
exit 2
fi

View File

@ -1,28 +0,0 @@
#!/bin/bash
source ~/openrc
# The login URL to access horizon.
# Example: http://example.com/auth/login/
HORIZON_URL="${1}"
# If you don't use regions, this will generally be OPENSTACK_KEYSTONE_URL in local_settings.py.
# View the source on the Horizon page for more information and search for "region".
REGION="${2}"
curl -sL -c /tmp/cookie.txt -b /tmp/cookie.txt $1 -o /dev/null
CSRF_TOKEN=$(grep csrftoken /tmp/cookie.txt | awk '{print $NF}')
TIMEOUT_CHECK=$(curl -sL -b /tmp/cookie.txt -d "username=${OS_USERNAME}" -d "password=${OS_PASSWORD}" -d "region=${REGION}" -d "csrfmiddlewaretoken=${CSRF_TOKEN}" -m 20 --referer ${HORIZON_URL} ${HORIZON_URL} | tee /tmp/horizon.txt)
LOGIN_CHECK=$(grep alert-error /tmp/horizon.txt)
rm /tmp/cookie.txt
rm /tmp/horizon.txt
if [ "${TIMEOUT_CHECK}" == "" ] || [ "${LOGIN_CHECK}" != "" ]; then
echo "CRITICAL - Horizon is inaccessible"
exit 2
fi
echo "Horizon Login OK"
exit 0

View File

@ -1,12 +0,0 @@
#!/bin/bash
source ~/openrc
keystone token-get 2>&1 > /dev/null
if [ $? == 0 ]; then
echo "OK - Got a Keystone token."
exit 0
else
echo "CRITICAL - Unable to get a Keystone token."
exit 2
fi

View File

@ -1,12 +0,0 @@
#!/bin/bash
# Uses sudo, so an entry in /etc/sudoers is most likely required.
sudo virsh list 2>&1 > /dev/null
if [ $? -eq 1 ]; then
echo "CRITICAL - problem with libvirt"
exit 3
else
vms=$(sudo virsh list | grep instance | wc -l)
echo "OK - ${vms} running"
exit 0
fi

View File

@ -1,16 +0,0 @@
#!/bin/bash
source ~/openrc
output=$(nova service-list | grep down)
if [ $? -eq 0 ]; then
echo -n "CRITICAL - OpenStack Nova services down: "
echo "${output}" | awk '{print $2,$4}' | while read LINE; do
echo -n "${LINE}; "
done
echo ""
exit 2
else
echo "OK - All nodes up"
exit 0
fi

View File

@ -1,10 +0,0 @@
#!/bin/bash
x=$(/usr/bin/curl -s https://${1}:8080/healthcheck)
if [ "${x}" == "OK" ]; then
echo "OK - Swift Proxy healthcheck is reporting ${x}"
exit 0
else
echo "CRITICAL - Swift Proxy healthcheck is reporting ${x}"
exit 3
fi

View File

@ -1,86 +0,0 @@
#!/usr/bin/env ruby
#
# Keystone API monitoring script for Sensu
#
# Copyright © 2014 Christopher Eckhardt
#
# Author: Christopher Eckhardt <djbkd@dreamsofelectricsheep.net>
#
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.
#
require 'rubygems' if RUBY_VERSION < '1.9.0'
require 'sensu-plugin/check/cli'
require 'net/http'
require 'net/https'
require 'json'
class CheckKeystoneAPI < Sensu::Plugin::Check::CLI
option :url, :short => '-u URL'
option :tenant, :short => '-T TENANT'
option :user, :short => '-U USERNAME'
option :pass, :short => '-P PASSWORD'
option :timeout, :short => '-t SECONDS', :proc => proc {|a| a.to_i}, :default => 10
def run
if config[:url]
uri = URI.parse(config[:url])
config[:host] = uri.host
config[:port] = uri.port
config[:path] = uri.path + '/tokens'
config[:ssl] = uri.scheme == 'https'
else
unless config[:host] and config[:path]
unknown 'No URL specified'
end
config[:port] ||= 5000
end
begin
timeout(config[:timeout]) do
request_token
end
rescue Timeout::Error
critical "Keystone API timed out"
rescue => e
critical "Keystone API Connection error: #{e.message}"
end
end
def request_token
conn = Net::HTTP.new(config[:host], config[:port])
if config[:ssl]
conn.use_ssl = true
conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
api_request = {
'auth' => {
'passwordCredentials' => {
'username' => config[:user],
'password' => config[:pass]
},
'tenantName' => config[:tenant]
}
}.to_json
req = Net::HTTP::Post.new(config[:path])
req.body = api_request
req['Content-Type'] = 'application/json'
res = conn.start{|http| http.request(req)}
case res.code
when /^2/
ok res.code + res.body
when /^[45]/
critical res.code + res.body
else
warning res.code + res.body
end
end
end