Skip to content
Snippets Groups Projects
Verified Commit cd951c10 authored by Quentin Duchemin's avatar Quentin Duchemin
Browse files

Add Wekan stat collection

parent bd964564
No related branches found
No related tags found
No related merge requests found
......@@ -19,6 +19,14 @@
"password" : "admin_password",
"name" : "instancename"
}
],
"wekan" : [
{
"url" : "https://my.wekan.tld",
"name" : "instancename",
"user": "admin_username",
"password": "admin_password"
}
]
}
}
main.py 100644 → 100755
......@@ -12,6 +12,7 @@ from urllib.parse import urlparse
from influxdb import InfluxDBClient
from etherpad import EtherpadCollector
from mattermost import MattermostCollector
from wekan import WekanCollector
# Some constants
DIR_NAME = os.path.dirname(os.path.realpath(__file__))
......@@ -49,7 +50,7 @@ def influxb_connect(config):
# Connect to influx db
try:
return InfluxDBClient(
client = InfluxDBClient(
host=o.hostname,
port=port,
username=config['user'],
......@@ -58,6 +59,8 @@ def influxb_connect(config):
ssl=ssl,
verify_ssl=verify
)
client.ping()
return client
except Exception as e:
print("Cannot connect to {} : {}".format(o.hostname, e))
print("If InfluxDB has just started, this is normal, please wait!")
......@@ -97,6 +100,13 @@ def main():
mattermost_data = mattermost.collect()
influx_client.write_points(mattermost_data, 'ms')
# Get Wekan metrics and push to InfluxDB
if 'wekan' in config['modules']:
wekan = WekanCollector(config['modules']['wekan'])
wekan_data = wekan.collect()
print(wekan_data)
influx_client.write_points(wekan_data, 'ms')
if __name__ == "__main__":
main()
......@@ -9,7 +9,7 @@ from urllib.parse import urlparse
from mattermostdriver import Driver
class MattermostCollector(object):
class MattermostCollector():
"""
MattermostCollector.
Collector for Mattermost stats.
......@@ -72,6 +72,7 @@ class MattermostCollector(object):
# Get all instances stats
for instance in self.instances:
# Get current stats
print("Mattermost : collecting for instance {}".format(instance['name']))
data = self._get_stats(instance)
if data is None:
print('Unable to get stats from Mattermost instance ' + instance['config']['url'])
......
# coding=utf-8
"""Package to collect Wekan metrics."""
from .wekan import WekanCollector
# coding=utf-8
"""Functions to export Wekan metrics."""
import json
import datetime
import requests
from urllib.parse import urlparse
import sys
class WekanCollector():
"""
WekanCollector.
Collector for Wekan stats.
"""
def __init__(self, config):
"""
Initialize a wekan collector object.
:param config: Configuration for Wekan module (list of instances)
"""
# Initialize list of instances connector
self.instances = []
for instance in config:
if 'url' not in instance or 'name' not in instance or 'user' not in instance or 'password' not in instance:
print('Incorrect instance configuration\n')
print(instance)
print('"wekan" key on configuration file should be a list of object with "url", "user", "password" and "name" attributes')
continue
else:
if instance['url'].endswith('/'):
instance['url'] = instance['url'][:-1]
instance['token'] = self._login(instance)
if instance['token']:
self.instances.append(instance)
def collect(self):
"""
Get the analytics of etherpad instances and returns a list of InfluxDB points.
:returns: List of InfluxDB formatted objects
"""
metrics = []
# Get all instances stats
for instance in self.instances:
print("Wekan : collecting for instance {}".format(instance['name']))
data = self._get_stats(instance)
# Get current miliseconds timestamp
current_timestamp = int(datetime.datetime.now().timestamp()*1000)
# Create metrics
metrics.append({
'measurement': 'wekan_total_users',
'tags': {
'name': instance['name']
},
'time': current_timestamp,
'fields': {
'value': data['totalUsers']
}
})
metrics.append({
'measurement': 'wekan_public_boards',
'tags': {
'name': instance['name']
},
'time': current_timestamp,
'fields': {
'value': data['publicBoards']
}
})
metrics.append({
'measurement': 'wekan_private_boards',
'tags': {
'name': instance['name']
},
'time': current_timestamp,
'fields': {
'value': data['privateBoards']
}
})
print("Wekan : data collected for instance {}".format(instance['name']))
return metrics
@classmethod
def _login(cls, instance):
"""
Login via the API to a Wekan instance.
:param instance: Configuration for Wekan instance (dict with at least "url", "user" and "password" keys)
:returns: Authentication token
"""
login = requests.post(instance['url'] + '/users/login',
data={
"username": instance['user'],
"password": instance['password']
})
if login.status_code != 200:
print('Unable to login to {} instance : {}'.format(instance['url'], login.reason))
return None
return login.json()['token']
@classmethod
def _get_stats(cls, instance):
"""
Get stats for an Wekan instance.
:param instance: Configuration for Wekan instance (dict with at least "url" and "token" key)
:returns: JSON data
"""
headers = {'Authorization': f'Bearer {instance["token"]}'}
data = {}
# Get user count
users = requests.get(instance['url'] + '/api/users', headers=headers).json()
# Users are a list, a JSON is generated if the request fails
# But the request itself will always send a HTTP 200, even if it fails...
if isinstance(users, dict) and users.get('statusCode', 200) != 200:
print('Unable to get users from {} instance : {}'.format(instance['url'], users['reason']))
data['totalUsers'] = 0
else:
data['totalUsers'] = len(users)
public_boards = requests.get(instance['url'] + '/api/boards', headers=headers).json()
if isinstance(public_boards, dict) and public_boards.get('statusCode', 200) != 200:
print('Unable to get public boards from {} instance : {}'.format(instance['url'], public_boards['reason']))
data['publicBoards'] = 0
else:
data['publicBoards'] = len(public_boards)
# API does not have a method to get all boards, so first get
# boards for each users
all_boards = []
for user in users:
boards = requests.get(instance['url'] + f'/api/users/{user["_id"]}/boards', headers=headers).json()
if isinstance(public_boards, dict) and public_boards.get('statusCode', 200) != 200:
print('Unable to get public boards from {} instance : {}'.format(instance['url'], public_boards['reason']))
else:
# Then remove default boards (we don't care)
for e in boards:
if e['title'] == 'Welcome Board' or e['title'] == 'Templates':
boards.remove(e)
all_boards.extend(boards)
# Finally filter unique boards
all_boards_unique = list({v['_id']: v for v in all_boards}.values())
# Then infer number of private boards from total boards - public boards
data['privateBoards'] = len(all_boards_unique) - len(public_boards)
return data
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment