Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Anze committed Jan 31, 2019
0 parents commit c33964c
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
38 changes: 38 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
image: docker:stable

services:
- docker:dind

variables:
DOCKER_DRIVER: overlay2

stages:
- deploy


deploy_to_docker_hub:
stage: deploy
when: manual
only:
# We only want master branch AND when tag looks like 'vX.Y.Z', however GitLab doesn't support conjunctive conditions yet:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/27818
# refs:
# - master # Yeah, that doesn't work... The job for a commit with a tag and on a master branch is not being created.
#
# However we can mark tags 'v*.*.*' as protected, which also allows us to (somewhat) safely use Private-Token as protected
# CI variable.
variables:
- $CI_COMMIT_TAG =~ /^v[0-9]+[.][0-9]+[.][0-9]+$/
script:
- apk add --no-cache git
# check that we are deploying the latest version:
- export LAST_KNOWN_VERSION=`git tag -l --sort=-version:refname "v*.*.*" | head -n 1 | tr -d '[:space:]'`
- '[ "$LAST_KNOWN_VERSION" == "$CI_COMMIT_TAG" ] || (echo "Tag does not denote latest known version (which is $LAST_KNOWN_VERSION), aborting!" && exit 1)'
- echo "Deploying..."
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG" -t "$CI_REGISTRY_IMAGE:latest" --build-arg VERSION=$CI_COMMIT_TAG --build-arg VCS_REF=$CI_COMMIT_SHA --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
- docker push "$CI_REGISTRY_IMAGE:latest"
- docker rmi grafolean/grafolean:$CI_COMMIT_TAG
- docker rmi grafolean/grafolean:latest

8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM python:3.6-alpine

COPY requirements.txt grafolean-worker-ping.py /opt/
RUN pip install -r /opt/requirements.txt
RUN echo -e "* * * * * source /etc/environment; export BACKEND_URL BOT_TOKEN; python /opt/grafolean-worker-ping.py > /proc/1/fd/1 2> /proc/1/fd/2\n" > /etc/crontabs/root

# https://stackoverflow.com/a/47960145/593487
ENTRYPOINT ["crond", "-f", "-d", "8"]
13 changes: 13 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
requests = "*"
multiping = "*"

[requires]
python_version = "3.6"
66 changes: 66 additions & 0 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '2.1'
services:

# To run this image, you must create a .env file with following content:
# BACKEND_URL=... # backend API base url (the part before "/api/...")
# BOT_TOKEN=... # bot token

grafolean-worker-ping:
# If you wish to load an explicit version, change the next line. For example:
# image: grafolean/grafolean-worker-ping:v1.0.0
image: grafolean/grafolean-worker-ping
build:
context: .
dockerfile: Dockerfile
volumes:
- .env:/etc/environment
87 changes: 87 additions & 0 deletions grafolean-worker-ping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from collections import defaultdict
from multiping import MultiPing
import os
import requests
import socket
import time

N_PINGS = 3

# This is copy-pasted from multiping package; the reason is that we need to get MultiPing
# instance, because it holds the IP addresses which correspond to the addresses we wanted
# pinged - and the key in ping results is the IP.
def multi_ping(dest_addrs, timeout, retry=0, ignore_lookup_errors=False):
retry_timeout = float(timeout) / (retry + 1)

mp = MultiPing(dest_addrs, ignore_lookup_errors=ignore_lookup_errors)

results = {}
retry_count = 0
while retry_count <= retry:
# Send a batch of pings
mp.send()
single_results, no_results = mp.receive(retry_timeout)
# Add the results from the last sending of pings to the overall results
results.update(single_results)
if not no_results:
# No addresses left? We are done.
break
retry_count += 1

return results, no_results, mp

def get_addr_for_ip_dict(addrs, mp):
# This is complicated, and a hack. Still... mp (MultiPing instance) holds two lists,
# self._dest_addrs and self._unprocessed_targets. List _unprocessed_targets has the addresses
# that couldn't be resolved. Others were resolved, and _dest_addrs has the IPs in the same
# order as original addresses.
resolved_addrs = [a for a in addrs if a not in mp._unprocessed_targets]
ip_to_addr = {k: v for k, v in zip(mp._dest_addrs, resolved_addrs)}
return ip_to_addr

def do_ping(addrs):
results = defaultdict(list)
mp = None
ip_to_addr = {}
for i in range(N_PINGS):
print(".")
responses, no_responses, mp = multi_ping(addrs, timeout=2, retry=3, ignore_lookup_errors=True)

# Some addresses (like demo.grafolean.com) resolve to multiple IPs, so each call to multi_ping will
# resolve differently - we must find the new IP addresses every time:
ip_to_addr = get_addr_for_ip_dict(addrs, mp)

for no_resp in no_responses:
addr = ip_to_addr.get(no_resp, no_resp)
results[addr].append(None)
for resp, t in responses.items():
addr = ip_to_addr.get(resp, resp)
results[addr].append(t)

if i < N_PINGS - 1:
time.sleep(1)
return dict(results)

def send_results_to_grafolean(base_url, account_id, bot_token, results):
url = '{}/api/accounts/{}/values/?b={}'.format(base_url, account_id, bot_token)
values = []
for ip in results:
for ping_index, ping_time in enumerate(results[ip]):
values.append({
'p': 'ping.{}.{}.success'.format(ip.replace('.', '_'), ping_index),
'v': 0 if ping_time is None else 1,
})
if ping_time is not None:
values.append({
'p': 'ping.{}.{}.rtt'.format(ip.replace('.', '_'), ping_index),
'v': ping_time,
})
print("Sending results to Grafolean")
r = requests.post(url, json=values)
print(r.text)
r.raise_for_status()

if __name__ == "__main__":
addrs = ["8.8.8.8", "youtube.com", "127.0.0.1", "demo.grafolean.com", "grafolean.com", "whateverdoeesndfexist.com"]
results = do_ping(addrs)
send_results_to_grafolean(os.environ.get('BACKEND_URL'), 1, os.environ.get('BOT_TOKEN'), results)
7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-i https://pypi.org/simple
certifi==2018.11.29
chardet==3.0.4
idna==2.8
multiping==1.1.2
requests==2.21.0
urllib3==1.24.1

0 comments on commit c33964c

Please sign in to comment.