Skip to content

Commit 412d097

Browse files
committed
Initial commit
1 parent 3a32760 commit 412d097

File tree

9 files changed

+199
-3
lines changed

9 files changed

+199
-3
lines changed

.gitignore

-3
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ target/
7272
# Jupyter Notebook
7373
.ipynb_checkpoints
7474

75-
# pyenv
76-
.python-version
77-
7875
# celery beat schedule file
7976
celerybeat-schedule
8077

.python-version

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2.7
2+
3.4.7
3+
3.6.3
4+
3.7.0a3

.travis.yaml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
sudo: false
2+
language: python
3+
python:
4+
- 2.7
5+
- 3.4
6+
- 3.6
7+
- 3.7-dev
8+
matrix:
9+
include:
10+
- python: 3.6
11+
env: TOXENV=flake8
12+
install:
13+
- pip install tox-travis
14+
- pip install coveralls
15+
script:
16+
- tox
17+
after_success: coveralls
18+
19+
deploy:
20+
provider: pypi
21+
distributions: "sdist bdist_wheel"
22+
skip_existing: true
23+
user: timbirk
24+
password:
25+
secure: HSxKxtAuR5ck6T/pbCwEBr346PdAkG5VE9t907K3fnKOChBUWdANRqHTmo3lw9TtfuoIfFhHpvMPF65tE/9OLiUl/hpzOHeZeB50rYq8XkGcPTYyjAyrCjDZ91/yHfOZMzG/sUgbT/37c0nf7LzYCLz+hlcQ1Xt9+GzAqKZ80cR/hZCYZmSSmgr6PNJEaZYYtD/MQnGB/gN5Rvefnj+ijVBB78GUxhxu0Ro9uO9IyaJSb8Fm6nJIgmiY8sXEenFxv9h43m4b4oRrv0kj+19b0ekYBj/ECJzPvYejiiJnb+cXtOcSfQlhH2kWgK0rxa2AmvwH0/9n4wwF5A8lvxls+Wh6NP/t5GSUe4VSduzNcV/CMfIytbXuShNPfAHLEPDZ/uGlj/e83VbNAAADocJl745U+B08k3f0J9x2n5Gm6gzbzWzkMn5DJTrqwYHtsdFkJKffdTFE5xpu6JBcL24I17n/j9t3ADibnnSN3L9TYOXSIiqWFEboP6Vl3tGbSHu+TiQgl/LuZ4XkiB9Cakcj0WPUvus0+0BuqDtrzUsy5cpSuaVGK8wTyJHdgMdwgNkNI3YwWHQt89nSQDGwZPAkdAsPLwukYl6s/6PtKMl9gblONN8JMm4Zpww0X5/wpwiUwwrRVEEyyTiI7dtqdgCMxeYgBrb8UX6aDqX5KlD6Jd4=
26+
on:
27+
tags: true

MANIFEST.in

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include *.py
2+
include README.md

grafannotate/__init__.py

Whitespace-only changes.

grafannotate/cli.py

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import sys
2+
import requests
3+
import click
4+
import logging
5+
import time
6+
import json
7+
8+
try:
9+
from urllib.parse import urlparse
10+
except ImportError:
11+
from urlparse import urlparse
12+
13+
@click.command()
14+
@click.option('-u', '--uri', 'annotate_uri',
15+
default='http://localhost:3000/api/annotations', help='uri to send annotation to')
16+
@click.option('-T', '--title', 'title', default='event', help='event title')
17+
@click.option('-t', '--tag', 'tags', multiple=True, help='event tags')
18+
@click.option('-d', '--description', 'description', help='event description')
19+
@click.option('-s', '--start', 'start_time', default=int(round(time.time() * 1000)), help='event start timestamp (unix secs)')
20+
@click.option('-e', '--end', 'end_time', default=0, help='event end timestamp (unix secs)')
21+
22+
def main(annotate_uri, title, tags, description, start_time, end_time):
23+
""" Send Grafana annotations """
24+
25+
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
26+
27+
if description is None:
28+
description = "".join([line for line in iter(sys.stdin.readline, '')])
29+
30+
tags_field = ';'.join(tags)
31+
32+
try:
33+
url_parts = urlparse(annotate_uri)
34+
event_data = {}
35+
36+
if 'http' in url_parts.scheme:
37+
event_data['text'] = '<b>%s</b>\n\n%s' % (title, description)
38+
event_data['tags'] = tags
39+
event_data['time'] = start_time
40+
if end_time > 0:
41+
if end_time < start_time:
42+
raise Exception('Event end time cannot be before start time')
43+
event_data['isRegion'] = True
44+
event_data['timeEnd'] = end_time
45+
46+
send_web_annotation(url_parts, event_data)
47+
48+
elif 'influx' in url_parts.scheme:
49+
event_data['name'] = 'events'
50+
event_data['columns'] = ['tags', 'text', 'title']
51+
event_data['points'] = [[tags_field, description, title]]
52+
53+
send_influx_annotation(url_parts, event_data)
54+
55+
else:
56+
raise NotImplementedError('Scheme %s not recognised in uri %s' %
57+
(url_parts.scheme, annotate_uri))
58+
59+
except Exception as e:
60+
logging.fatal(e)
61+
# We could exit 1 here but we really don't want to cause a job to
62+
# fail just because we couldn't send an event.
63+
64+
sys.exit(0)
65+
66+
def send_web_annotation(url_parts, event_data):
67+
""" POST event to an endpoint in Grafana Annotations API format """
68+
logging.info('Sending web event to %s' % url_parts.hostname)
69+
#logging.debug(json.dumps(event_data))
70+
71+
url = url_parts.geturl()
72+
auth_tuple = None
73+
74+
if url_parts.username and url_parts.password:
75+
auth_tuple = (url_parts.username, url_parts.password)
76+
url_host_port = url_parts.netloc.split('@')[1]
77+
url = '%s://%s%s' % (url_parts.scheme, url_host_port, url_parts.path)
78+
79+
post_result = requests.post(url, json=json.dumps(event_data),
80+
auth=auth_tuple, timeout=5)
81+
82+
if post_result.status_code > 299:
83+
logging.error('Received %s response, sending event failed' % post_result.status_code)
84+
85+
if 'message' in post_result:
86+
logging.info(post_result.message)
87+
88+
def send_influx_annotation(url_parts, event_data):
89+
raise NotImplementedError('Influx annotations not yet implemented, check back soon.')
90+
logging.info('Sending influx event to %s' % url_parts.hostname)
91+
logging.debug(json.dumps(event_data))

setup.cfg

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[wheel]
2+
universal = 1

setup.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# python-publicholiday
2+
# ---------------
3+
# A fast, efficient command line utility for working with public holidays
4+
#
5+
# Author: Tim Birkett <[email protected]>
6+
# Website: https://github.com/devopsmakers/grafannotate
7+
# License: MIT License (see LICENSE file)
8+
9+
import codecs
10+
from setuptools import find_packages, setup
11+
12+
dependencies = [
13+
'click==6.7',
14+
'requests==2.21.0'
15+
]
16+
17+
setup(
18+
name='grafannotate',
19+
version='0.0.1',
20+
url='https://github.com/devopsmakers/grafannotate',
21+
license='MIT',
22+
author='Tim Birkett',
23+
author_email='[email protected]',
24+
description='Send annotations to Grafana',
25+
long_description=codecs.open('README.md', encoding='utf-8').read(),
26+
long_description_content_type='text/markdown',
27+
packages=find_packages(exclude=['tests']),
28+
include_package_data=True,
29+
zip_safe=False,
30+
platforms='any',
31+
install_requires=dependencies,
32+
entry_points={
33+
'console_scripts': [
34+
'grafannotate = grafannotate.cli:main',
35+
],
36+
},
37+
classifiers=[
38+
# As from http://pypi.python.org/pypi?%3Aaction=list_classifiers
39+
# 'Development Status :: 1 - Planning',
40+
# 'Development Status :: 2 - Pre-Alpha',
41+
# 'Development Status :: 3 - Alpha',
42+
'Development Status :: 4 - Beta',
43+
# 'Development Status :: 5 - Production/Stable',
44+
# 'Development Status :: 6 - Mature',
45+
# 'Development Status :: 7 - Inactive',
46+
'Topic :: Utilities',
47+
'Environment :: Console',
48+
#'Intended Audience :: Developers',
49+
'Intended Audience :: Information Technology',
50+
'Intended Audience :: System Administrators',
51+
'License :: MIT License',
52+
'Operating System :: OS Independent',
53+
'Programming Language :: Python',
54+
'Programming Language :: Python :: 2',
55+
'Programming Language :: Python :: 3',
56+
#'Topic :: Software Development :: Libraries :: Python Modules',
57+
]
58+
)

tox.ini

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[tox]
2+
envlist=py27, py34, py36, py37, flake8
3+
4+
[testenv]
5+
commands=py.test --cov grafannotate {posargs}
6+
deps=
7+
pytest
8+
pytest-cov
9+
10+
[testenv:flake8]
11+
basepython = python3.6
12+
deps =
13+
flake8
14+
commands =
15+
flake8 grafannotate tests --max-line-length=120

0 commit comments

Comments
 (0)