diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..52cd589 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include prometheus-swift-exporter.service +include post.sh diff --git a/README.md b/README.md index 5d92a03..ba47596 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Install prometheus_client: pip install prometheus_client ``` -## Installation +## Manual Installation ``` # Copy example config in place, edit to your needs @@ -42,6 +42,13 @@ Or to run interactively: Configuration options are documented in prometheus-swift-exporter.yaml shipped with this project +# To build rpm + +``` +python setup.py bdist_rpm --post-install post.sh +``` + +This installs the systemd service file but not the prometheus-swift-exporter.yaml # FAQ ## Why hardcode swift host list? diff --git a/post.sh b/post.sh new file mode 100644 index 0000000..b03cbf0 --- /dev/null +++ b/post.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +touch /etc/default/prometheus-swift-exporter +/usr/bin/systemctl enable prometheus-swift-exporter.service diff --git a/prometheus-swift-exporter b/prometheus-swift-exporter old mode 100755 new mode 100644 index 9a432bf..aba6942 --- a/prometheus-swift-exporter +++ b/prometheus-swift-exporter @@ -26,10 +26,12 @@ from os import path import traceback import urlparse import requests +import json as simplejson from BaseHTTPServer import BaseHTTPRequestHandler from BaseHTTPServer import HTTPServer from SocketServer import ForkingMixIn -from prometheus_client import CollectorRegistry, generate_latest, Gauge, CONTENT_TYPE_LATEST +from prometheus_client import CollectorRegistry, generate_latest, \ + Gauge, CONTENT_TYPE_LATEST import logging import logging.handlers @@ -40,88 +42,142 @@ log = logging.getLogger('poe-logger') class Swift(): def __init__(self): self.registry = CollectorRegistry() - self.baseurl = 'http://{{}}:{}/recon/{{}}'.format(config.get('swift_port','6000')) + self.baseurl = 'http://{{}}:{}/recon/{{}}'.format( + config.get('swift_port', '9002')) self.swift_hosts = config.get('swift_hosts', []) + def gen_get(self, h, p): + r = requests.get(self.baseurl.format(h, 'diskusage')) + if r.status_code != 200: + raise requests.exceptions.RequestException + return r + def gen_up_stats(self): labels = ['cloud', 'hostname'] swift_up = Gauge('swift_host_up', 'Swift host reachability', labels, registry=self.registry) for h in self.swift_hosts: try: - requests.get(self.baseurl.format(h, 'diskusage')) + r = self.gen_get(h, 'diskusage') swift_up.labels(config['cloud'], h).set(1) except requests.exceptions.RequestException: swift_up.labels(config['cloud'], h).set(0) def gen_disk_usage_stats(self): labels = ['cloud', 'hostname', 'device', 'type'] - swift_disk = Gauge('swift_disk_usage_bytes', 'Swift disk usage in bytes', - labels, registry=self.registry) + swift_disk = Gauge( + 'swift_disk_usage_bytes', + 'Swift disk usage in bytes', + labels, + registry=self.registry) for h in self.swift_hosts: try: - r = requests.get(self.baseurl.format(h, 'diskusage')) - except requests.exceptions.RequestException: + r = self.gen_get(h, 'diskusage') + except requests.exceptions.RequestException as ValueError: continue for disk in r.json(): - if not all([disk.get(i, False) for i in ['size', 'used', 'device']]): + if not all([disk.get(i, False) + for i in ['size', 'used', 'device']]): continue - swift_disk.labels(config['cloud'], h, disk['device'], 'size').set(int(disk['size'])) - swift_disk.labels(config['cloud'], h, disk['device'], 'used').set(int(disk['used'])) + swift_disk.labels(config['cloud'], + h, disk['device'], + 'size').set(int(disk['size'])) + swift_disk.labels(config['cloud'], + h, disk['device'], + 'used').set(int(disk['used'])) def gen_quarantine_stats(self): labels = ['cloud', 'hostname', 'ring'] - swift_quarantine = Gauge('swift_quarantined_objects', 'Number of quarantined objects', - labels, registry=self.registry) + swift_quarantine = Gauge( + 'swift_quarantined_objects', + 'Number of quarantined objects', + labels, + registry=self.registry) for h in self.swift_hosts: try: - r = requests.get(self.baseurl.format(h, 'quarantined')) + r = self.gen_get(h, 'quarantined') except requests.exceptions.RequestException: continue for ring in ['accounts', 'objects', 'containers']: - swift_quarantine.labels(config['cloud'], h, ring).set(r.json().get(ring)) + if (isinstance(r.json(), dict)): + swift_quarantine.labels( + config['cloud'], h, ring).set( + r.json().get(ring)) + + def gen_unmounted_stats(self): + labels = ['cloud', 'hostname', 'device'] + swift_unmounted = Gauge('swift_umounted_disks', 'Disk is unmounted', + labels, registry=self.registry) + for h in self.swift_hosts: + try: + r = self.gen_get(h, 'unmounted') + except requests.exceptions.RequestException: + continue + for disk in r.json(): + if disk.get('mounted', True) is False: + swift_unmounted.labels(config['cloud'], + h, disk['device']).set(1) def gen_replication_stats(self): labels = ['cloud', 'hostname', 'ring', 'type'] - swift_repl = Gauge('swift_replication_stats', 'Swift replication stats', labels, registry=self.registry) + swift_repl = Gauge( + 'swift_replication_stats', + 'Swift replication stats', + labels, + registry=self.registry) labels = ['cloud', 'hostname', 'ring'] - swift_repl_duration = Gauge('swift_replication_duration_seconds', 'Swift replication duration in seconds', - labels, registry=self.registry) + swift_repl_duration = Gauge( + 'swift_replication_duration_seconds', + 'Swift replication duration in seconds', + labels, + registry=self.registry) for h in self.swift_hosts: metrics = ['attempted', 'diff', 'diff_capped', 'empty', 'failure', 'hashmatch', 'no_change', 'remote_merge', 'remove', 'rsync', 'success', 'ts_repl'] # Object replication is special try: - r = requests.get(self.baseurl.format(h, 'replication/object')) + r = self.gen_get(h, 'replication/object') except requests.exceptions.RequestException: continue try: - swift_repl_duration.labels(config['cloud'], h, 'object').set(r.json()['object_replication_time']) + swift_repl_duration.labels( + config['cloud'], h, 'object').set( + r.json()['object_replication_time']) except TypeError: - print(traceback.format_exc()) + continue for ring in ['account', 'container']: try: - r = requests.get(self.baseurl.format(h, 'replication/' + ring)) + r = self.gen_get(h, 'replication/' + ring) except requests.exceptions.RequestException: continue try: - swift_repl_duration.labels(config['cloud'], h, ring).set(r.json()['replication_time']) + if (r.json()['replication_time']): + swift_repl_duration.labels( + config['cloud'], h, ring).set( + r.json()['replication_time']) except TypeError: print(traceback.format_exc()) - for metric in metrics: - try: - swift_repl.labels(config['cloud'], h, ring, metric).set(r.json()['replication_stats'][metric]) - except TypeError: - print(traceback.format_exc()) + if r.json().get('replication_stats'): + for metric in metrics: + try: + swift_repl.labels( + config['cloud'], h, ring, metric).set( + r.json()['replication_stats'][metric]) + except TypeError: + print("M", metric) + print("C", config['cloud'], h, ring, metric) + print("R", r.json()['replication_stats']) + print(traceback.format_exc()) def get_stats(self): self.gen_up_stats() self.gen_disk_usage_stats() self.gen_quarantine_stats() self.gen_replication_stats() + self.gen_unmounted_stats() return generate_latest(self.registry) @@ -144,7 +200,7 @@ class OpenstackSwiftExporterHandler(BaseHTTPRequestHandler): self.send_header('Content-Type', CONTENT_TYPE_LATEST) self.end_headers() self.wfile.write(output) - except: + except BaseException: self.send_response(500) self.end_headers() self.wfile.write(traceback.format_exc()) @@ -168,15 +224,16 @@ def handler(*args, **kwargs): if __name__ == '__main__': - parser = argparse.ArgumentParser(usage=__doc__, - description='Prometheus OpenStack Swift exporter', - formatter_class=argparse.RawTextHelpFormatter) + parser = argparse.ArgumentParser( + usage=__doc__, + description='Prometheus OpenStack Swift exporter', + formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('config_file', nargs='?', help='Configuration file path', default='/etc/prometheus/prometheus-swift-exporter.yaml', type=argparse.FileType('r')) args = parser.parse_args() - log.setLevel(logging.DEBUG) + # log.setLevel(logging.DEBUG) for logsock in ('/dev/log', '/var/run/syslog'): if path.exists(logsock): log.addHandler(logging.handlers.SysLogHandler(address=logsock)) diff --git a/prometheus-swift-exporter.service b/prometheus-swift-exporter.service index e3a92b0..df98f55 100644 --- a/prometheus-swift-exporter.service +++ b/prometheus-swift-exporter.service @@ -4,7 +4,7 @@ After=network.target [Service] EnvironmentFile=/etc/default/prometheus-swift-exporter -ExecStart=/opt/prometheus-swift-exporter/prometheus-swift-exporter /etc/prometheus/prometheus-swift-exporter.yaml +ExecStart=/usr/bin/prometheus-swift-exporter KillMode=process [Install] diff --git a/setup.py b/setup.py index f81c09c..3aae6d5 100644 --- a/setup.py +++ b/setup.py @@ -12,10 +12,11 @@ def read(fname): description="Exposes high level OpenStack Swift metrics to Prometheus.", license="GPLv3", keywords=["prometheus", "openstack", "swift", "exporter"], - url="https://github.com/ilanddev/prometheus-swift-exporter", + url="https://github.com/vorsprung/prometheus-swift-exporter", scripts=["prometheus-swift-exporter"], + data_files=[("/etc/systemd/system",["prometheus-swift-exporter.service"])], install_requires=["prometheus_client"], - long_description=read('README.md'), + long_description="Exposes high level OpenStack Swift metrics to Prometheus.", classifiers=[ "Development Status :: 4 - Beta", "Topic :: System :: Networking :: Monitoring",