#!/usr/bin/env python3 # -*- coding: utf-8 -*- import ipaddress from api import EjabberdApi # rfc6052: IPv6 Addressing of IPv4/IPv6 Translators nat64 = ipaddress.ip_network("64:ff9b::/96") class EjabberdMetrics: """ class to fetch metrics per xmlrpc """ def __init__(self, url, login=None, api="rpc", muc_host: str = 'conference'): # init ejabberd api self.api = EjabberdApi(url, login, api) self._cmd = self.api.cmd # variables self._verstring = self.api.verstring self.muc_host = muc_host def _client(self, resource): clientmap = { "Conv6ations": ["Conversations with IPv6", "Conv6ations for Sum7"], "Conversations": [], "Pix-Art Messenger": [], "Gajim": ["gajim"], "Psi+": [], "jitsi": [], "Dino": ["dino"], "poezio": [], "profanity": [], "Xabber": ["xabber", "xabber-android"], "ChatSecure": ["chatsecure"], "Monal": [] } for client, names in clientmap.items(): for c in names: if c in resource: return client if client in resource: return client return "other" @staticmethod def _ipversion(ip): addr = ipaddress.ip_address(ip) if addr.version == 6: if addr.ipv4_mapped: return 4 if addr in nat64: return 4 return addr.version def fetch_onlineuser(self): tmp = self._cmd("connected_users_info", {}) if "connected_users_info" not in tmp: return tmp data = [] for c in tmp["connected_users_info"]: if "session" not in c: continue user = {} for attrs in c["session"]: for k, v in attrs.items(): user[k] = v data.append(user) return data def fetch_nodes(self): result = self._cmd("list_cluster", {}) if "nodes" not in result: return result data = [] for node in result["nodes"]: data.append(node["node"]) return data def fetch_vhosts(self): result = self._cmd("registered_vhosts", {}) if "vhosts" not in result: return result data = [] for vhost in result["vhosts"]: data.append(vhost["vhost"]) return data def fetch_s2s_in(self): result = self._cmd("incoming_s2s_number", {}) if "s2s_incoming" not in result: return result return result["s2s_incoming"] def fetch_s2s_out(self): result = self._cmd("outgoing_s2s_number", {}) if "s2s_outgoing" not in result: return result return result["s2s_outgoing"] def fetch_registered(self, vhost=None): if vhost is None: result = self._cmd("stats", {"name":"registeredusers"}) if "stat" in result: return result["stat"] else: result = self._cmd("stats_host", {"name":"registeredusers", "host": vhost}) if "stat" in result: return result["stat"] def fetch_muc(self, vhost=None): host = "global" if vhost is not None: if self._verstring.major >= 19: host = '.'.join([self.muc_host, vhost]) else: host = vhost result = self._cmd("muc_online_rooms", {"host": host}) if "rooms" in result: return len(result["rooms"]) return len(result) def update(self): # nodes self._nodes = self.fetch_nodes() # vhosts self._vhosts = self.fetch_vhosts() # registered if not hasattr(self, "_registered"): self._registered = {} self._registered[None] = self.fetch_registered() # muc if not hasattr(self, "_muc"): self._muc = {} self._muc[None] = self.fetch_muc() # registered + muc for vhost in self._vhosts: self._registered[vhost] = self.fetch_registered(vhost) self._muc[vhost] = self.fetch_muc(vhost) # online user self._onlineuser = self.fetch_onlineuser() # s2s self._s2s_in = self.fetch_s2s_in() self._s2s_out = self.fetch_s2s_out() def get_online_by(self, by="node", parse=None, vhost=None, node=None): parser = parse or (lambda a: a) if not hasattr(self, "_onlineuser"): self._onlineuser = self.fetch_onlineuser() data = {} for conn in self._onlineuser: if vhost is not None and vhost not in conn["jid"]: continue if node is not None and node != conn["node"]: continue if by not in conn: continue value = parser(conn[by]) if value not in data: data[value] = 1 else: data[value] += 1 return data def get_online_by_node(self, vhost=None): return self.get_online_by("node", vhost=vhost) def get_online_by_vhost(self, node=None): return self.get_online_by("jid", parse=lambda jid: jid[jid.find("@")+1:jid.find("/")], node=node) def get_online_by_status(self, vhost=None, node=None): return self.get_online_by("status", vhost=vhost, node=node) def get_online_by_connection(self, vhost=None, node=None): return self.get_online_by("connection", vhost=vhost, node=node) def get_online_by_client(self, vhost=None, node=None): return self.get_online_by("resource", parse=self._client, vhost=vhost, node=node) def get_online_by_ipversion(self, vhost=None, node=None): return self.get_online_by("ip", parse=self._ipversion, vhost=vhost, node=node) def get_online_client_by(self, by="ip", parse=None, vhost=None, node=None): parser = parse or self._ipversion if not hasattr(self, "_onlineuser"): self._onlineuser = self.fetch_onlineuser() data = {} for conn in self._onlineuser: client = "other" if "resource" in conn: client = self._client(conn["resource"]) if client not in data: data[client] = {} if vhost is not None and vhost not in conn["jid"]: continue if node is not None and node != conn["node"]: continue if by not in conn: continue value = parser(conn[by]) if value not in data[client]: data[client][value] = 1 else: data[client][value] += 1 return data def get_online_client_by_ipversion(self, vhost=None, node=None): return self.get_online_client_by("ip", parse=self._ipversion, vhost=vhost, node=node) def get_registered(self, vhost=None): if not hasattr(self, "_registered"): self._registered = {} if vhost not in self._registered: self._registered[vhost] = self.fetch_registered(vhost) return self._registered[vhost] def get_muc(self, vhost=None): if not hasattr(self, "_muc"): self._muc = {} if vhost not in self._muc: self._muc[vhost] = self.fetch_muc(vhost) return self._muc[vhost] def get_vhosts(self): if not hasattr(self, "_vhosts"): self._vhosts = self.fetch_vhosts() return self._vhosts def get_s2s_in(self): if not hasattr(self, "_s2s_in"): self._s2s_in = self.fetch_s2s_in() return self._s2s_in def get_s2s_out(self): if not hasattr(self, "_s2s_out"): self._s2s_out = self.fetch_s2s_out() return self._s2s_out def get_vhost_metrics(self, vhost): data = { "registered": self.get_registered(vhost), "muc": self.get_muc(vhost), "online_by_status": self.get_online_by_status(vhost), "online_by_client": self.get_online_by_client(vhost), "online_by_ipversion": self.get_online_by_ipversion(vhost), "online_by_connection": self.get_online_by_connection(vhost), "online_by_node": self.get_online_by_node(vhost) } return data def get_nodes(self): if not hasattr(self, "_nodes"): self._nodes = self.fetch_nodes() return self._nodes def get_node_metrics(self, node): data = { "online_by_status": self.get_online_by_status(node=node), "online_by_client": self.get_online_by_client(node=node), "online_by_ipversion": self.get_online_by_ipversion(node=node), "online_by_connection": self.get_online_by_connection(node=node), "online_by_vhost": self.get_online_by_vhost(node=node) } return data def get_all(self): data = { "registered": self.get_registered(), "muc": self.get_muc(), "online_by_status": self.get_online_by_status(), "online_by_client": self.get_online_by_client(), "online_by_ipversion": self.get_online_by_ipversion(), "online_by_connection": self.get_online_by_connection(), "online_by_node": self.get_online_by_node(), "online_by_vhost": self.get_online_by_vhost() } vhosts = {} for host in self.get_vhosts(): vhosts[host] = self.get_vhost_metrics(host) data["vhosts"] = vhosts nodes = {} for node in self.get_nodes(): nodes[node] = self.get_node_metrics(node) data["online_client_by_ipversion"] = self.get_online_client_by_ipversion() data["nodes"] = nodes data["s2s_in"] = self.get_s2s_in() data["s2s_out"] = self.get_s2s_out() return data if __name__ == "__main__": import json from config import Config # load config config = Config() # credentials and parameters url = config.get('url', default='http://localhost:5280/api') login = config.get('login', default=None) api = config.get('api', default='rest') # init handler metrics = EjabberdMetrics(url, login, api) data = metrics.get_all() print(json.dumps(data, indent=True))