From 1e33aaa2d30526f2889539aec3e557ac0c5890d5 Mon Sep 17 00:00:00 2001 From: mfenjiro <mohamed.fenjiro@savoirfairelinux.com> Date: Thu, 11 Apr 2019 17:05:30 -0400 Subject: [PATCH] Monitor Jami Reliability Change-Id: I719a83badfb811ab9bb11dc01d29ce9363829573 --- tools/dringctrl/controller.py | 68 ++++++++++++------- tools/dringctrl/dringctrl.py | 2 +- tools/dringctrl/jami_test.py | 119 ++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 23 deletions(-) create mode 100755 tools/dringctrl/jami_test.py diff --git a/tools/dringctrl/controller.py b/tools/dringctrl/controller.py index baaa6cb824..d00b3f7d47 100644 --- a/tools/dringctrl/controller.py +++ b/tools/dringctrl/controller.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 # # Copyright (C) 2015-2019 Savoir-faire Linux Inc. Inc # @@ -28,22 +29,14 @@ import hashlib from threading import Thread from functools import partial - -try: - from gi.repository import GObject -except ImportError as e: - import gobject as GObject -except Exception as e: - print(str(e)) - exit(1) - -from errors import * +from errorsDring import DRingCtrlAccountError, DRingCtrlError, DRingCtrlDBusError, DRingCtrlDeamonError +from gi.repository import GLib try: import dbus from dbus.mainloop.glib import DBusGMainLoop except ImportError as e: - raise DRingCtrlError("No python-dbus module found") + raise DRingCtrlError(str(e)) DBUS_DEAMON_OBJECT = 'cx.ring.Ring' @@ -69,9 +62,7 @@ class DRingCtrl(Thread): self.isStop = False # Glib MainLoop for processing callbacks - self.loop = GObject.MainLoop() - - GObject.threads_init() + self.loop = GLib.MainLoop() # client registered to sflphoned ? self.registered = False @@ -94,7 +85,7 @@ class DRingCtrl(Thread): bus = dbus.SessionBus() except dbus.DBusException as e: - raise DRingCtrlDBusError("Unable to connect DBUS session bus") + raise DRingCtrlDBusError(str(e)) if not bus.name_has_owner(DBUS_DEAMON_OBJECT) : raise DRingCtrlDBusError(("Unable to find %s in DBUS." % DBUS_DEAMON_OBJECT) @@ -167,12 +158,18 @@ class DRingCtrl(Thread): def onCallHangup_cb(self, callId): pass - def onCallRinging_cb(self): + def onCallConnecting_cb(self, callId): + pass + + def onCallRinging_cb(self, callId): pass def onCallHold_cb(self): pass + def onCallInactive_cb(self): + pass + def onCallCurrent_cb(self): pass @@ -182,6 +179,9 @@ class DRingCtrl(Thread): def onCallFailure_cb(self): pass + def onCallOver_cb(self): + pass + def onIncomingCall(self, account, callid, to): """ On incoming call event, add the call to the list of active calls """ @@ -192,13 +192,18 @@ class DRingCtrl(Thread): self.onIncomingCall_cb(callid) - def onCallHangUp(self, callid): + def onCallHangUp(self, callid, state): """ Remove callid from call list """ + self.activeCalls[callid]['State'] = state self.onCallHangup_cb(callid) self.currentCallId = "" - del self.activeCalls[callid] + def onCallConnecting(self, callid, state): + """ Update state for this call to Ringing """ + + self.activeCalls[callid]['State'] = state + self.onCallConnecting_cb(callid) def onCallRinging(self, callid, state): """ Update state for this call to Ringing """ @@ -220,6 +225,11 @@ class DRingCtrl(Thread): self.activeCalls[callid]['State'] = state self.onCallCurrent_cb() + def onCallInactive(self, callid, state): + """ Update state for this call to current """ + + self.activeCalls[callid]['State'] = state + self.onCallInactive_cb() def onCallBusy(self, callid, state): """ Update state for this call to busy """ @@ -231,9 +241,14 @@ class DRingCtrl(Thread): def onCallFailure(self, callid, state): """ Handle call failure """ + self.activeCalls[callid]['State'] = state self.onCallFailure_cb() - del self.activeCalls[callid] + def onCallOver(self, callid): + """ Handle call failure """ + + self.onCallOver_cb() + del self.activeCalls[callid] def onCallStateChanged(self, callid, state, code): """ On call state changed event, set the values for new calls, @@ -252,7 +267,9 @@ class DRingCtrl(Thread): self.currentCallId = callid if state == "HUNGUP": - self.onCallHangUp(callid) + self.onCallHangUp(callid, state) + elif state == "CONNECTING": + self.onCallConnecting(callid, state) elif state == "RINGING": self.onCallRinging(callid, state) elif state == "CURRENT": @@ -263,6 +280,10 @@ class DRingCtrl(Thread): self.onCallBusy(callid, state) elif state == "FAILURE": self.onCallFailure(callid, state) + elif state == "OVER": + self.onCallOver(callid) + elif state == "INACTIVE": + self.onCallInactive(callid,state) else: print("unknown state:" + str(state)) @@ -498,7 +519,7 @@ class DRingCtrl(Thread): for call in self.activeCalls: print("\t" + call) - def Call(self, dest): + def Call(self, dest, account=None): """Start a call and return a CallID Use the current account previously set using setAccount(). @@ -508,7 +529,7 @@ class DRingCtrl(Thread): """ if dest is None or dest == "": - raise SflPhoneError("Invalid call destination") + raise DRingCtrlError("Invalid call destination") # Set the account to be used for this call if not self.account: @@ -638,6 +659,9 @@ class DRingCtrl(Thread): def sendFile(self, *args, **kwds): return self.configurationmanager.sendFile(*args, **kwds) + def sendTextMessage(self, account, to, message): + return self.configurationmanager.sendTextMessage(account, to, { 'text/plain': message }) + def run(self): """Processing method for this thread""" diff --git a/tools/dringctrl/dringctrl.py b/tools/dringctrl/dringctrl.py index b09df07bed..e5d842bc69 100755 --- a/tools/dringctrl/dringctrl.py +++ b/tools/dringctrl/dringctrl.py @@ -32,7 +32,7 @@ except Exception as e: print(str(e)) exit(1) -from errors import * + from controller import DRingCtrl from tester import DRingTester diff --git a/tools/dringctrl/jami_test.py b/tools/dringctrl/jami_test.py new file mode 100755 index 0000000000..f848121335 --- /dev/null +++ b/tools/dringctrl/jami_test.py @@ -0,0 +1,119 @@ +#! /usr/bin/env python3 +# +# Copyright (C) 2019 Savoir-faire Linux Inc. Inc +# +# Author: Mohamed Fenjiro <mohamed.fenjiro@savoirfairelinux.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import signal +import sys +import os +import time +import argparse + +from gi.repository import GLib +from errorsDring import DRingCtrlError +from controller import DRingCtrl + +class JamiTest(DRingCtrl): + def __init__(self, name, args): + super(JamiTest, self).__init__(name, False) + self.args = args + self.testCalls = set() + self.iterator = 0 + self.failureCount = 0 + self.failureRate = 0 + self.callsCompleted = 0 + ringAccounts = self.getAllAccounts('RING') + self.setAccount(ringAccounts[0]) + + if not args.peer: + peer = ringAccounts[1] + details = self.getAccountDetails(peer) + self.peer = details['Account.username'] + else: + self.peer = args.peer + + print("Using local test account: ", self.account) + print("Using test peer: ", self.peer) + if self.testCall(): + GLib.timeout_add_seconds(args.interval, self.testCall) + + def keepGoing(self): + return self.args.calls == 0 or self.iterator < self.args.calls + + def testCall(self): + print("**[BEGIN] Call Test") + if self.keepGoing(): + self.iterator += 1 + callId = self.Call(self.peer) + self.testCalls.add(callId) + GLib.timeout_add_seconds(self.args.duration, lambda: self.checkCall(callId)) + return self.keepGoing() + + def checkCall(self, callId): + self.HangUp(callId) + if callId in self.testCalls: + self.testFailed(callId) + return False + + def onCallStateChanged(self, callId, state, statecode): + super().onCallStateChanged(callId, state, statecode) + if callId in self.testCalls: + if state == "RINGING": + self.testSucceeded(callId) + self.HangUp(callId) + + def testEnded(self, callId): + self.testCalls.remove(callId) + self.callsCompleted += 1 + self.failureRate = (self.failureCount / float(self.callsCompleted)) + print("Failure rate: ", self.failureRate) + if not self.keepGoing(): + print("ENDING") + self.stopThread() + self.unregister() + + def testFailed(self, callId): + self.failureCount += 1 + self.testEnded(callId) + + def testSucceeded(self, callId): + self.testEnded(callId) + + def run(self): + super().run() + if self.failureCount == 0: + sys.exit(0) + elif self.failureRate < .5: + sys.exit(1) + else: + sys.exit(2) + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Monitor Jami reliabilty by mesuring failure rate for making Calls/Messages and receiving them.') + parser.add_argument('--messages', help='Number of messages sent', type=int) + parser.add_argument('--calls', help='Number of calls made', default=0, type=int) + parser.add_argument('--duration', help='Specify the duration of the test (seconds)', default=10, type=int) + parser.add_argument('--interval', help='Specify the test interval (seconds)', default=0, type=int) + parser.add_argument('--peer', help='Specify the peer account id') + + args = parser.parse_args() + + test = JamiTest("test", args) + test.run() \ No newline at end of file -- GitLab