yate/modules/server/monitoring.cpp

4090 lines
118 KiB
C++

/**
* monitoring.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Module for monitoring and gathering information about YATE.
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* 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.
*/
#include <yatephone.h>
#define SIP_PORT 5060
using namespace TelEngine;
namespace {
class Monitor;
class MsgUpdateHandler;
class CdrHandler;
class HangupHandler;
class CallMonitor;
// structure to hold a counter, a threshold for the counter
// and an alarm for when the threshold had been surpassed
typedef struct {
unsigned int counter;
unsigned int threshold;
bool alarm;
} BaseInfo;
// container for MGCP transaction information
typedef struct {
BaseInfo transactions; // MGCP transactions information
BaseInfo deletes; // MGCP delete connection transactions that have timed out
u_int64_t reset; // interval after which the data of this structure should be reset
u_int64_t resetTime; // time at which the data should be reset
bool gw_monitor;
} MGCPInfo;
// container for SIP transaction information
typedef struct {
BaseInfo auths; // SIP authentication requests information
BaseInfo transactions; // SIP timed out transactions information
BaseInfo byes; // SIP timed out BYE transactions information
u_int64_t reset;
u_int64_t resetTime;
} SIPInfo;
/**
* Class Cache
* BaseClass for retaining and expiring different type of data
*/
class Cache : public Mutex, public GenObject
{
public:
enum Info {
COUNT = 1,
INDEX = 2,
};
inline Cache(const char* name)
: Mutex(false,name),
m_reload(true), m_expireTime(0), m_retainInfoTime(0)
{ }
virtual ~Cache();
// set the time which is used for increasing the expire time for the cache at each access
inline void setRetainInfoTime(u_int64_t time)
{
m_retainInfoTime = time;
m_expireTime = 0; //expire data
}
// get information from the cached data
virtual String getInfo(const String& query, unsigned int& index, TokenDict* dict);
// check if the information has expired
inline bool isExpired()
{ return Time::secNow() > m_expireTime; }
// update the time at which the data will expire
inline void updateExpire()
{
m_expireTime = Time::secNow() + m_retainInfoTime;
m_reload = false;
}
protected:
// load data into this object from a engine.status message
virtual bool load();
// discard the cached data
virtual void discard();
// table containing information about modules obtained from an engine.status message
ObjList m_table;
// flag for reloading
bool m_reload;
private:
// time at which the cached data will expire (in seconds)
u_int64_t m_expireTime;
// value with which increase the expire time at each access
u_int64_t m_retainInfoTime;
};
/**
* Class ActiveCallInfo
* Hold data about current calls
*/
class ActiveCallsInfo : public Cache
{
public:
enum InfoType {
COUNT = 1, // count of current call
INDEX = 2, // index of a call
ID = 3, // id of a call
STATUS = 4, // status of a call
CALLER = 5, // caller party
CALLED = 6, // called party
PEER = 7, // peer(s) channel of a call
DURATION = 8, // call duration
};
// Constructor
inline ActiveCallsInfo()
: Cache("Monitor::activeCallsInfo")
{ }
// Destructor
inline ~ActiveCallsInfo()
{ }
// add information about peers by checking the billing ID
String checkPeers(const String& billID, const String& callID);
protected:
// load data into this object from a engine.status message
bool load();
};
/**
* Class SigInfo
* Base class for handling information from the signalling channel module about signalling components
*/
class SigInfo : public Cache
{
public:
// enum for types of information
enum InfoType {
COUNT = 1, // number of components
INDEX = 2, // index of a component
ID = 3, // id of a component
STATUS = 4, // status of a component
TYPE = 5, // the type of the component
ALARMS_COUNT = 6, // alarm counter for the component
SKIP = 7, // helper value to skip unnecessary information when parsing the status string
};
// Constructor
inline SigInfo(const char* name, const TokenDict* dict)
: Cache(name), m_dictionary(dict)
{ }
// Destructor
inline ~SigInfo()
{ m_table.clear(); }
// update the alarm counter for the component with the given name
void updateAlarmCounter(const String& name);
protected:
// load data into this object from a engine.status message
virtual bool load();
virtual void discard();
// dictionary for
const TokenDict* m_dictionary;
};
/**
* Class InterfaceInfo
*/
class InterfaceInfo : public SigInfo
{
public:
// Constructor
inline InterfaceInfo()
: SigInfo("Monitor::ifaceInfo",s_ifacesInfo)
{ }
// Destructor
inline ~InterfaceInfo()
{ }
// Dictionary for mapping Monitor queries about signalling interfaces
static TokenDict s_ifacesInfo[];
protected:
// load data into this object from a engine.status message
bool load();
};
/**
* Class LinkInfo
*/
class LinkInfo : public SigInfo
{
public:
enum LinkExtraInfo {
UPTIME = 8,
};
// Constructor
inline LinkInfo()
: SigInfo("Monitor::linkInfo",s_linkInfo)
{ }
// Destructor
inline ~LinkInfo()
{ }
// dictionary for mapping Monitor queries
static TokenDict s_linkInfo[];
protected:
// load data into this object from a engine.status message
bool load();
};
/**
* Class LinksetInfo
* Hold status data about signalling linksets
*/
class LinksetInfo : public SigInfo
{
public:
inline LinksetInfo()
: SigInfo("Monitor::linksetInfo",s_linksetInfo)
{ }
inline ~LinksetInfo()
{ }
// dictionary for mapping Monitor queries
static TokenDict s_linksetInfo[];
protected:
// load data into this object from a engine.status message
bool load();
// parse individual link entries
NamedList* parseLinksetInfo(String& info, const String& link, NamedList* infoFill = 0);
// dictonary for mapping status parameters
static TokenDict s_linksetStatus[];
};
/**
* Class TrunkInfo
* Hold status data about signalling trunks
*/
class TrunkInfo : public SigInfo
{
public:
enum TrunkExtraInfo {
CIRCUITS = 7,
CALLS = 8,
LOCKED = 9,
IDLE = 10,
};
// Constructor
inline TrunkInfo()
: SigInfo("Monitor::trunkInfo",s_trunkInfo)
{ }
// Destructor
inline ~TrunkInfo()
{ }
// dictionary for mapping Monitor queries
static TokenDict s_trunkInfo[];
protected:
// load data into this object from a engine.status message
bool load();
virtual void discard();
// parse individual trunk entries
NamedList* parseTrunkInfo(String& info,const String& trunkName, NamedList* infoFill = 0);
// dictonary for mapping status parameters
static TokenDict s_trunkStatus[];
};
/**
* Accounts Info
* Cache for account status information
*/
class AccountsInfo : public Cache
{
public:
// account information type
enum AccountInfoType {
COUNT = 1,
INDEX = 2,
ID = 3,
STATUS = 4,
PROTO = 5,
USERNAME = 6,
};
// Constructor
inline AccountsInfo()
: Cache("Monitor::accountsInfo")
{ }
// Destructor
inline ~AccountsInfo()
{ }
private:
// load data into this object from a engine.status message
bool load();
};
/**
* EngineInfo - engine status information cache
*/
class EngineInfo : public Cache
{
public:
enum EngineInfoType {
ENGINE_TYPE = 1,
ENGINE_PLUGINS = 2,
ENGINE_HANDLERS = 3,
ENGINE_MESSAGES = 4,
ENGINE_THREADS = 5,
ENGINE_WORKERS = 6,
ENGINE_MUTEXES = 7,
ENGINE_LOCKS = 8,
ENGINE_SEMAPHORES = 9,
ENGINE_WAITING = 10,
ENGINE_RUNATTEMPT = 11,
ENGINE_NODENAME = 12,
ENGINE_STATE = 13,
ENGINE_CALL_ACCEPT = 14,
ENGINE_UNEX_RESTART = 15,
ENGINE_MAXQUEUED = 16,
ENGINE_MSGRATE = 17,
ENGINE_MAXMSGRATE = 18,
ENGINE_MSGENQUEUED = 19,
ENGINE_MSGDEQUEUED = 20,
ENGINE_MSGDISPATCHED = 21,
};
// Constructor
inline EngineInfo()
: Cache("Monitor::engineInfo")
{ }
// Destructor
inline ~EngineInfo()
{ }
// get information from the cached data. Reimplemented from Cache
String getInfo(const String query, unsigned int index, TokenDict* dict);
private:
// load data into this object from a engine.status message
bool load();
// dictionary for mapping engine status parameters
static TokenDict s_engineInfo[];
};
/**
* ModuleInfo - module information cache
*/
class ModuleInfo : public Cache
{
public:
enum ModuleInfoType {
COUNT = 1,
INDEX = 2,
MODULE_NAME = 3,
MODULE_TYPE = 4,
MODULE_INFO = 5,
MODULE_FORMAT = 6,
};
// Constructor
inline ModuleInfo()
: Cache("Monitor::moduleInfo")
{ }
// Destructor
inline ~ModuleInfo()
{ }
private:
// load data into this object from a engine.status message
bool load();
// dictionary for mapping engine status parameters
static TokenDict s_moduleInfo[];
};
/**
* DatabaseAccount
* A container which holds status information about a single database account
*/
class DatabaseAccount : public GenObject
{
public:
enum DbIndex {
TOTAL_IDX = 0, // index for total number of queries
FAILED_IDX = 1, // index for number of failed queries
ERROR_IDX = 2, // index for number of queries returning with an error status
TIME_IDX = 3, // index for time spent executing queries
CONN_IDX = 4, // index for number of active connections
};
enum DbAlarms {
TOTAL_ALARM = 0x1,
FAILED_ALARM = 0x2,
ERROR_ALARM = 0x4,
EXEC_ALARM = 0x8,
CONNS_ALARM = 0x10,
};
// enum for information type
enum DbData {
QueriesCount = 1,
FailedCount = 2,
ErrorsCount = 3,
ExecTime = 4,
TooManyAlrm = 5,
TooManyFailedAlrm = 6,
TooManyErrorAlrm = 7,
ExecTooLongAlrm = 8,
NoConnAlrm = 9,
TooManyAlrmCount = 10,
TooManyFailedAlrmCount = 11,
TooManyErrorAlrmCount = 12,
ExecTooLongAlrmCount = 13,
NoConnAlrmCount = 14,
MaxQueries = 15,
MaxFailedQueries = 16,
MaxErrorQueries = 17,
MaxExecTime = 18,
AccountName = 19,
AccountIndex = 20,
};
// Constructor
DatabaseAccount(const NamedList* cfg);
// Destructor
inline ~DatabaseAccount()
{ }
// reimplemented toString() method to make object searcheble in lists
inline const String& toString() const
{ return m_name; }
// set the database entry index in the database account table
inline void setIndex(unsigned int index)
{ m_index = index; }
// get this account's index
inline unsigned int index()
{ return m_index; }
// update the internal data from the list received
void update(const NamedList& info);
// obtain data
const String getInfo(unsigned int query);
// reset internal data
void reset();
inline bool isCurrent()
{ return m_isCurrent; }
inline void setIsCurrent(bool current = true)
{ m_isCurrent = current; }
// update configuration for this direction
void updateConfig(const NamedList* cfg);
private:
// account name
String m_name;
// index
unsigned int m_index;
// counters for number of queries
unsigned int m_dbCounters[ExecTime];
// counters for previous interval counter values
unsigned int m_prevDbCounters[ExecTime];
// alarms set
u_int16_t m_alarms;
// alarm counters
unsigned int m_alarmCounters[CONN_IDX + 1];
// thresholds for triggering alarms
unsigned int m_thresholds[CONN_IDX];
// time at which internal data should be reset
unsigned int m_resetTime;
// time to hold on on current data
unsigned int m_resetInterval;
// flag if monitored account is current (i.e. false means this direction was removed from monitoring)
bool m_isCurrent;
};
/**
* DatabaseInfo - database information
* A list of DatabaseAccounts
*/
class DatabaseInfo : public Cache
{
public:
enum DbInfoType {
Connections = 1,
FailedConns = 2,
Accounts = 3,
};
// Constructor
inline DatabaseInfo(bool monitored = false)
: Cache("Monitor::dbInfo"), m_monitor(monitored)
{ }
// Destructor
inline ~DatabaseInfo()
{ m_table.clear(); }
// create and add a new DatabaseAccount created from the received configuration
void addDatabase(NamedList* cfg);
// update the internal data from the received message
void update(const Message& msg);
// get the requested information
String getInfo(const String& query, unsigned int& index, TokenDict* dict);
// try to reset internal data of the table entries
void reset();
inline void setMonitorEnabled(bool enable = false)
{ m_monitor = enable; }
inline bool monitorEnable()
{ return m_monitor; }
// reconfigure
void setConfigure(const NamedList* sect);
// update database account after reinitialization
void updateDatabaseAccounts();
private:
static TokenDict s_databaseInfo[];
bool load();
// number of successful and failed connections to databases
unsigned int m_connData[FailedConns];
bool m_monitor;
static String s_dbParam;
static String s_totalParam;
static String s_failedParam;
static String s_errorParam;
static String s_hasConnParam;
static String s_timeParam;
};
/**
* RTPEntry. Container holding data about a single monitored RTP direction
*/
class RTPEntry : public GenObject
{
public:
enum RTPInfoType {
Count = 1,
Index = 2,
Direction = 3,
NoAudio = 4,
LostAudio = 5,
PktsLost = 6,
SyncLost = 7,
SeqLost = 8,
WrongSRC = 9,
WrongSSRC = 10,
};
RTPEntry(String rtpDirection);
~RTPEntry();
inline const String& toString() const
{ return m_rtpDir; }
// update the entry from the received information
void update(const NamedList& nl);
// reset internal data
void reset();
// set the index for this entry
inline void setIndex(unsigned int index)
{ m_index = index; }
// get info from thins entry
String getInfo(unsigned int query);
// mapping dictionary for Monitor queries
static TokenDict s_rtpInfo[];
inline bool isCurrent()
{ return m_isCurrent; }
inline void setIsCurrent(bool current = true)
{ m_isCurrent = current; }
private:
// the RTP direction
String m_rtpDir;
// counters
unsigned int m_counters[WrongSSRC - Direction];
unsigned int m_index;
// flag if monitored direction is current (i.e. false means this direction was removed from monitoring)
bool m_isCurrent;
};
/**
* RTPTable. A list of RTPEntry
*/
class RTPTable : public GenObject
{
public:
// Constructor
inline RTPTable(const NamedList* cfg);
// Destructor
inline ~RTPTable()
{ m_rtpEntries.clear(); }
// update internal data
void update(Message& msg);
// get the answer to a query
String getInfo(const String& query, const unsigned int& index);
// reset internal data
void reset();
// check if the internal data should be reset
inline bool shouldReset()
{ return Time::secNow() >= m_resetTime; }
void reconfigure(const NamedList* cfg);
private:
// list of RTPEntry
ObjList m_rtpEntries;
Mutex m_rtpMtx;
// interval for how long the data should be kept
u_int64_t m_resetInterval;
// time at which the data should be reset
u_int64_t m_resetTime;
// RTP monitored?
bool m_monitor;
};
// A route entry which is monitored for quality of service values
class CallRouteQoS : public GenObject
{
public:
enum CallStatus {
ANSWERED = 1,
DELIVERED = 2,
};
enum Indexes {
CURRENT_IDX = 0,
PREVIOUS_IDX = 1,
TOTAL_IDX = 2,
};
enum ALARMS {
LOW_ASR = 1,
HIGH_ASR = 2,
LOW_NER = 4,
};
enum QoSNotifs {
ASR_LOW = 1,
ASR_HIGH = 2,
ASR_LOW_ALL = 3,
ASR_HIGH_ALL = 4,
NER_LOW = 5,
NER_LOW_ALL = 6,
ASR = 7,
NER = 8,
ASR_ALL = 9,
NER_ALL = 10,
MIN_ASR = 11,
MAX_ASR = 12,
MIN_NER = 13,
LOW_ASR_COUNT = 14,
HIGH_ASR_COUNT = 15,
LOW_ASR_ALL_COUNT = 16,
HIGH_ASR_ALL_COUNT = 17,
LOW_NER_COUNT = 18,
LOW_NER_ALL_COUNT = 19,
HANGUP = 40,
REJECT = 41,
BUSY = 42,
CANCELLED = 43,
NO_ANSWER = 44,
NO_ROUTE = 45,
NO_CONN = 46,
NO_AUTH = 47,
CONGESTION = 48,
NO_MEDIA = 49,
NO_CAUSE = 50,
HANGUP_ALL = 60,
REJECT_ALL = 61,
BUSY_ALL = 62,
CANCELLED_ALL = 63,
NO_ANSWER_ALL = 64,
NO_ROUTE_ALL = 65,
NO_CONN_ALL = 66,
NO_AUTH_ALL = 67,
CONGESTION_ALL = 68,
NO_MEDIA_ALL = 69,
NAME = 80,
INDEX = 81,
};
// Constructor
CallRouteQoS(const String direction, const NamedList* cfg = 0);
// Destructor
~CallRouteQoS();
// update the call counters and call end reason counters
void update(int type = -1, int endReason = -1);
// update the ASR and NER values
void updateQoS();
// reset the internal data
void reset();
// check if the given value has surpassed the given threshold and set the appropriate alarm
void checkForAlarm(int& value, float hysteresis, u_int8_t& alarm, const int min, const int max, u_int8_t minAlarm, u_int8_t maxAlarm = 0xff);
// is this route in a state of alarm
bool alarm();
// get the alarm
const String alarmText();
// send periodic notifications
void sendNotifs(unsigned int index, bool reset = false);
// get the response to the given query
bool get(int query, String& result);
// reimplemented toString() method to make the object searcheable in lists
inline const String& toString() const
{ return m_routeName; }
// set the index of this object
inline void setIndex(unsigned int index)
{ m_index = index; }
inline unsigned int index()
{ return m_index; }
inline bool isCurrent()
{ return m_isCurrent; }
inline void setIsCurrent(bool current = true)
{ m_isCurrent = current; }
// update configuration for this direction
void updateConfig(const NamedList* cfg);
private:
String m_routeName;
// call hangup reasons counters
unsigned int m_callCounters[NO_CAUSE - HANGUP];
unsigned int m_callCountersAll[NO_CAUSE - HANGUP];
unsigned int m_totalCalls[TOTAL_IDX + 1]; // total calls
unsigned int m_answeredCalls[TOTAL_IDX + 1]; // total answered calls
unsigned int m_delivCalls[TOTAL_IDX + 1]; // total delivered calls
// alarm flags for avoiding sending multiple times the same alarm
u_int8_t m_alarms;
u_int8_t m_overallAlarms;
// flags for keeping track if an alarm has been sent or not
u_int8_t m_alarmsSent;
u_int8_t m_overallAlarmsSent;
// alarm thresholds
int m_minASR;
int m_maxASR;
int m_minNER;
// alarm counters
unsigned int m_alarmCounters[NER_LOW_ALL + 1];
// minimum number of calls before starting
unsigned int m_minCalls;
// index in the table
unsigned int m_index;
// flag if monitored direction is current (i.e. false means this direction was removed from monitoring)
bool m_isCurrent;
};
/**
* Class CallMonitor
* Monitors number of calls, termination causes, computes statistic data
*/
class CallMonitor : public MessageHandler, public Thread
{
public:
enum Indexes {
IN_ASR_Idx = 0,
OUT_ASR_Idx = 1,
IN_NER_Idx = 2,
OUT_NER_Idx = 3,
};
enum Queries {
INCOMING_CALLS = 9,
OUTGOING_CALLS = 10,
ROUTES_COUNT = 11,
};
// constructor
CallMonitor(const NamedList* sect, unsigned int priority = 100);
virtual ~CallMonitor()
{ }
// inherited methods
virtual bool received(Message& msg);
virtual bool init();
virtual void run();
// obtain the value from the monitored data
void get(const String& query, const int& index, String& result);
// get the value of a call counter
bool getCounter(int type, unsigned int& value);
// send an alarm from a route
void sendAlarmFrom(CallRouteQoS* route);
// add a route to be monitored
void addRoute(NamedList* sect);
// reconfigure
void setConfigure(const NamedList* sect);
// update route after reinitialization
void updateRoutes();
private:
// interval at which notifications are sent
unsigned int m_checkTime;
// time at which notifications are sent
unsigned int m_notifTime;
// call counters
unsigned int m_inCalls;
unsigned int m_outCalls;
// list of routes
ObjList m_routes;
Mutex m_routesMtx;
bool m_first;
// parameter on which to select routes from call.cdr message
String m_routeParam;
// Directions monitored?
bool m_monitor;
// configure mutex
Mutex m_cfgMtx;
};
class SnmpMsgHandler;
class EngineStartHandler;
class AuthHandler;
class RegisterHandler;
/**
* Class Monitor
* Monitoring module
*/
class Monitor : public Module
{
public:
// enum for notification categories
enum Categories {
CALL_MONITOR = 1,
DATABASE = 2,
ALARM_COUNTERS = 3,
ACTIVE_CALLS = 4,
PSTN = 5,
ENGINE = 6,
MODULE = 7,
AUTH_REQUESTS = 8,
REGISTER_REQUESTS = 9,
INTERFACE = 10,
SIP = 11,
RTP = 12,
TRUNKS = 13,
LINKSETS = 14,
LINKS = 15,
IFACES = 16,
ACCOUNTS = 17,
MGCP = 18,
};
enum SigTypes {
SS7_MTP3 = 1,
TRUNK = 2,
ISDN = 3,
};
enum Cards {
InterfaceDown = 1,
InterfaceUp,
};
enum SigNotifs {
TrunkDown = 1,
TrunkUp,
LinksetDown,
LinksetUp,
LinkDown,
LinkUp,
IsdnQ921Down,
IsdnQ921Up,
};
enum SipNotifs {
TransactTimedOut = 1,
FailedAuths,
ByesTimedOut,
GWTimeout,
GWUp,
DeletesTimedOut,
};
Monitor();
virtual ~Monitor();
//inherited methods
virtual void initialize();
virtual bool received(Message& msg, int id);
bool unload();
// handle module.update messages
void update(Message& msg);
// read configuration file
void readConfig(const Configuration& cfg);
// build and send SS7 notifications
void sendSigNotifs(Message& msg);
// build and send physical interface notifications
void sendCardNotifs(Message& msg);
// handle MGCP & SIP status notifications
void checkNotifs(Message& msg, unsigned int type);
// build a notification message
void sendTrap(const String& trap, const String& value, unsigned int index = 0,
const char* text = 0);
// send multiple notifications at once
void sendTraps(const NamedList& traps);
// handle a monitor.query message
bool solveQuery(Message& msg);
// update monitored SIP gateway information
void handleChanHangup(const String& address, int& cause);
bool verifyGateway(const String& address);
// obtain SIP/MGCP transactions info
String getTransactionsInfo(const String& query, const int who);
private:
// message handlers
MsgUpdateHandler* m_msgUpdateHandler;
SnmpMsgHandler* m_snmpMsgHandler;
HangupHandler* m_hangupHandler;
EngineStartHandler* m_startHandler;
CallMonitor* m_callMonitor;
AuthHandler* m_authHandler;
RegisterHandler* m_registerHandler;
bool m_init;
bool m_newTraps;
// list of monitored SIP gateways and timed out gateways
ObjList* m_sipMonitoredGws;
ObjList m_timedOutGws;
// flags if certain monitored information should be passed along in form of notifications
bool m_trunkMon;
bool m_linksetMon;
bool m_linkMon;
bool m_interfaceMon;
bool m_isdnMon;
// caches
ActiveCallsInfo* m_activeCallsCache;
TrunkInfo* m_trunkInfo;
EngineInfo* m_engineInfo;
ModuleInfo* m_moduleInfo;
DatabaseInfo* m_dbInfo;
RTPTable* m_rtpInfo;
LinksetInfo* m_linksetInfo;
LinkInfo* m_linkInfo;
InterfaceInfo* m_ifaceInfo;
AccountsInfo* m_accountsInfo;
};
static int s_yateRun = 0;
static int s_yateRunAlarm = 0;
static int s_alarmThreshold = DebugNote;
static String s_nodeState = "";
static double s_qosHysteresisFactor = 2.0;
MGCPInfo s_mgcpInfo = { {0, 0, false}, {0, 0, false}, 0, 0, false};
static SIPInfo s_sipInfo = { {0, 0, false}, {0, 0, false}, {0, 0, false}, 0, 0};
static TokenDict s_modules[] = {
{"mysqldb", Monitor::DATABASE},
{"pgsqldb", Monitor::DATABASE},
{"sig", Monitor::PSTN},
{"wanpipe", Monitor::INTERFACE},
{"zaptel", Monitor::INTERFACE},
{"Tdm", Monitor::INTERFACE},
{"sip", Monitor::SIP},
{"yrtp", Monitor::RTP},
{"mgcpca", Monitor::MGCP},
{0,0}
};
static TokenDict s_categories[] = {
// database info
{"databaseCount", Monitor::DATABASE},
{"databaseIndex", Monitor::DATABASE},
{"databaseAccount", Monitor::DATABASE},
{"queriesCount", Monitor::DATABASE},
{"failedQueries", Monitor::DATABASE},
{"errorQueries", Monitor::DATABASE},
{"queryExecTime", Monitor::DATABASE},
{"successfulConnections", Monitor::DATABASE},
{"failedConnections", Monitor::DATABASE},
// database alarm counters
{"tooManyQueriesAlarms", Monitor::DATABASE},
{"tooManyFailedQueriesAlarms", Monitor::DATABASE},
{"tooManyErrorQueriesAlarms", Monitor::DATABASE},
{"queryExecTooLongAlarms", Monitor::DATABASE},
{"noConnectionAlarms", Monitor::DATABASE},
// database thresholds
{"queriesCountThreshold", Monitor::DATABASE},
{"failedQueriesThreshold", Monitor::DATABASE},
{"errorQueriesThreshold", Monitor::DATABASE},
{"queryExecTimeThreshold", Monitor::DATABASE},
// QOS
{"qosDirectionsCount", Monitor::CALL_MONITOR},
{"qosEntryIndex", Monitor::CALL_MONITOR},
{"qosEntryDirection", Monitor::CALL_MONITOR},
{"lowASRThreshold", Monitor::CALL_MONITOR},
{"highASRThreshold", Monitor::CALL_MONITOR},
{"currentASR", Monitor::CALL_MONITOR},
{"overallASR", Monitor::CALL_MONITOR},
{"lowNERThreshold", Monitor::CALL_MONITOR},
{"currentNER", Monitor::CALL_MONITOR},
{"overallNER", Monitor::CALL_MONITOR},
// QOS alarm counters
{"currentLowASRAlarmCount", Monitor::CALL_MONITOR},
{"overallLowASRAlarmCount", Monitor::CALL_MONITOR},
{"currentHighASRAlarmCount", Monitor::CALL_MONITOR},
{"overallHighASRAlarmCount", Monitor::CALL_MONITOR},
{"currentLowNERAlarmCount", Monitor::CALL_MONITOR},
{"overallLowNERAlarmCount", Monitor::CALL_MONITOR},
// call counters
{"incomingCalls", Monitor::CALL_MONITOR},
{"outgoingCalls", Monitor::CALL_MONITOR},
{"currentHangupEndCause", Monitor::CALL_MONITOR},
{"currentBusyEndCause", Monitor::CALL_MONITOR},
{"currentRejectedEndCause", Monitor::CALL_MONITOR},
{"currentCancelledEndCause", Monitor::CALL_MONITOR},
{"currentNoAnswerEndCause", Monitor::CALL_MONITOR},
{"currentNoRouteEndCause", Monitor::CALL_MONITOR},
{"currentNoConnectionEndCause", Monitor::CALL_MONITOR},
{"currentNoAuthEndCause", Monitor::CALL_MONITOR},
{"currentCongestionEndCause", Monitor::CALL_MONITOR},
{"currentNoMediaEndCause", Monitor::CALL_MONITOR},
{"overallHangupEndCause", Monitor::CALL_MONITOR},
{"overallBusyEndCause", Monitor::CALL_MONITOR},
{"overallRejectedEndCause", Monitor::CALL_MONITOR},
{"overallCancelledEndCause", Monitor::CALL_MONITOR},
{"overallNoAnswerEndCause", Monitor::CALL_MONITOR},
{"overallNoRouteEndCause", Monitor::CALL_MONITOR},
{"overallNoConnectionEndCause", Monitor::CALL_MONITOR},
{"overallNoAuthEndCause", Monitor::CALL_MONITOR},
{"overallCongestionEndCause", Monitor::CALL_MONITOR},
{"overallNoMediaEndCause", Monitor::CALL_MONITOR},
// connections info
// linksets
{"linksetCount", Monitor::LINKSETS},
{"linksetIndex", Monitor::LINKSETS},
{"linksetID", Monitor::LINKSETS},
{"linksetType", Monitor::LINKSETS},
{"linksetStatus", Monitor::LINKSETS},
{"linksetDownAlarms", Monitor::LINKSETS},
// links
{"linkCount", Monitor::LINKS},
{"linkIndex", Monitor::LINKS},
{"linkID", Monitor::LINKS},
{"linkType", Monitor::LINKS},
{"linkStatus", Monitor::LINKS},
{"linkDownAlarms", Monitor::LINKS},
{"linkUptime", Monitor::LINKS},
// interfaces
{"interfacesCount", Monitor::IFACES},
{"interfaceIndex", Monitor::IFACES},
{"interfaceID", Monitor::IFACES},
{"interfaceStatus", Monitor::IFACES},
{"interfaceDownAlarms", Monitor::IFACES},
// accounts
{"accountsCount", Monitor::ACCOUNTS},
{"accountIndex", Monitor::ACCOUNTS},
{"accountID", Monitor::ACCOUNTS},
{"accountStatus", Monitor::ACCOUNTS},
{"accountProtocol", Monitor::ACCOUNTS},
{"accountUsername", Monitor::ACCOUNTS},
// active calls info
{"activeCallsCount", Monitor::ACTIVE_CALLS},
{"callEntryIndex", Monitor::ACTIVE_CALLS},
{"callEntryID", Monitor::ACTIVE_CALLS},
{"callEntryStatus", Monitor::ACTIVE_CALLS},
{"callEntryCaller", Monitor::ACTIVE_CALLS},
{"callEntryCalled", Monitor::ACTIVE_CALLS},
{"callEntryPeerChan", Monitor::ACTIVE_CALLS},
{"callEntryDuration", Monitor::ACTIVE_CALLS},
// trunk info
{"trunksCount", Monitor::TRUNKS},
{"trunkIndex", Monitor::TRUNKS},
{"trunkID", Monitor::TRUNKS},
{"trunkType", Monitor::TRUNKS},
{"trunkCircuitCount", Monitor::TRUNKS},
{"trunkCurrentCallsCount", Monitor::TRUNKS},
{"trunkDownAlarms", Monitor::TRUNKS},
{"trunkCircuitsLocked", Monitor::TRUNKS},
{"trunkCircuitsIdle", Monitor::TRUNKS},
// engine info
{"plugins", Monitor::ENGINE},
{"handlers", Monitor::ENGINE},
{"messages", Monitor::ENGINE},
{"msgMaxQueued", Monitor::ENGINE},
{"msgLastSecond", Monitor::ENGINE},
{"msgMaxPerSecond", Monitor::ENGINE},
{"msgEnqueued", Monitor::ENGINE},
{"msgDequeued", Monitor::ENGINE},
{"msgDispatched", Monitor::ENGINE},
{"threads", Monitor::ENGINE},
{"workers", Monitor::ENGINE},
{"mutexes", Monitor::ENGINE},
{"locks", Monitor::ENGINE},
{"semaphores", Monitor::ENGINE},
{"waitingSemaphores", Monitor::ENGINE},
{"acceptStatus", Monitor::ENGINE},
{"unexpectedRestart", Monitor::ENGINE},
// node info
{"runAttempt", Monitor::ENGINE},
{"name", Monitor::ENGINE},
{"state", Monitor::ENGINE},
// module info
{"moduleCount", Monitor::MODULE},
{"moduleIndex", Monitor::MODULE},
{"moduleName", Monitor::MODULE},
{"moduleType", Monitor::MODULE},
{"moduleExtra", Monitor::MODULE},
// request stats
{"authenticationRequests", Monitor::AUTH_REQUESTS},
{"registerRequests", Monitor::REGISTER_REQUESTS},
// rtp stats
{"rtpDirectionsCount", Monitor::RTP},
{"rtpEntryIndex", Monitor::RTP},
{"rtpDirection", Monitor::RTP},
{"noAudioCounter", Monitor::RTP},
{"lostAudioCounter", Monitor::RTP},
{"packetsLost", Monitor::RTP},
{"syncLost", Monitor::RTP},
{"sequenceNumberLost", Monitor::RTP},
{"wrongSRC", Monitor::RTP},
{"wrongSSRC", Monitor::RTP},
// sip stats
{"transactionsTimedOut", Monitor::SIP},
{"failedAuths", Monitor::SIP},
{"byesTimedOut", Monitor::SIP},
// mgcp stats
{"mgcpTransactionsTimedOut", Monitor::MGCP},
{"deleteTransactionsTimedOut", Monitor::MGCP},
{0,0}
};
static TokenDict s_callQualityQueries[] = {
// alarms
{"currentLowASR", CallRouteQoS::ASR_LOW},
{"overallLowASR", CallRouteQoS::ASR_LOW_ALL},
{"currentHighASR", CallRouteQoS::ASR_HIGH},
{"overallHighASR", CallRouteQoS::ASR_HIGH_ALL},
{"currentLowNER", CallRouteQoS::NER_LOW},
{"overallLowNER", CallRouteQoS::NER_LOW_ALL},
{"qosEntryDirection", CallRouteQoS::NAME},
{"qosEntryIndex", CallRouteQoS::INDEX},
// notifications
{"currentASR", CallRouteQoS::ASR},
{"overallASR", CallRouteQoS::ASR_ALL},
{"currentNER", CallRouteQoS::NER},
{"overallNER", CallRouteQoS::NER_ALL},
// end cause counters
{"currentHangupEndCause", CallRouteQoS::HANGUP},
{"currentBusyEndCause", CallRouteQoS::BUSY},
{"currentRejectedEndCause", CallRouteQoS::REJECT},
{"currentCancelledEndCause", CallRouteQoS::CANCELLED},
{"currentNoAnswerEndCause", CallRouteQoS::NO_ANSWER},
{"currentNoRouteEndCause", CallRouteQoS::NO_ROUTE},
{"currentNoConnectionEndCause", CallRouteQoS::NO_CONN},
{"currentNoAuthEndCause", CallRouteQoS::NO_AUTH},
{"currentCongestionEndCause", CallRouteQoS::CONGESTION},
{"currentNoMediaEndCause", CallRouteQoS::NO_MEDIA},
{"overallHangupEndCause", CallRouteQoS::HANGUP_ALL},
{"overallBusyEndCause", CallRouteQoS::BUSY_ALL},
{"overallRejectedEndCause", CallRouteQoS::REJECT_ALL},
{"overallCancelledEndCause", CallRouteQoS::CANCELLED_ALL},
{"overallNoAnswerEndCause", CallRouteQoS::NO_ANSWER_ALL},
{"overallNoRouteEndCause", CallRouteQoS::NO_ROUTE_ALL},
{"overallNoConnectionEndCause", CallRouteQoS::NO_CONN_ALL},
{"overallNoAuthEndCause", CallRouteQoS::NO_AUTH_ALL},
{"overallCongestionEndCause", CallRouteQoS::CONGESTION_ALL},
{"overallNoMediaEndCause", CallRouteQoS::NO_MEDIA_ALL},
// thresholds
{"lowASRThreshold", CallRouteQoS::MIN_ASR},
{"highASRThreshold", CallRouteQoS::MAX_ASR},
{"lowNERThreshold", CallRouteQoS::MIN_NER},
// alarm counters
{"currentLowASRAlarmCount", CallRouteQoS::LOW_ASR_COUNT},
{"currentHighASRAlarmCount", CallRouteQoS::HIGH_ASR_COUNT},
{"overallLowASRAlarmCount", CallRouteQoS::LOW_ASR_ALL_COUNT},
{"overallHighASRAlarmCount", CallRouteQoS::HIGH_ASR_ALL_COUNT},
{"currentLowNERAlarmCount", CallRouteQoS::LOW_NER_COUNT},
{"overallLowNERAlarmCount", CallRouteQoS::LOW_NER_ALL_COUNT},
{0,0}
};
// call end reasons
static TokenDict s_endReasons[] = {
{"User hangup", CallRouteQoS::HANGUP},
{"Rejected", CallRouteQoS::REJECT},
{"rejected", CallRouteQoS::REJECT},
{"User busy", CallRouteQoS::BUSY},
{"busy", CallRouteQoS::BUSY},
{"Request Terminated", CallRouteQoS::NO_ANSWER},
{"noanswer", CallRouteQoS::NO_ANSWER},
{"No route to call target", CallRouteQoS::NO_ROUTE},
{"noroute", CallRouteQoS::NO_ROUTE},
{"Service Unavailable", CallRouteQoS::NO_CONN},
{"noconn", CallRouteQoS::NO_CONN},
{"service-unavailable", CallRouteQoS::NO_CONN},
{"Unauthorized", CallRouteQoS::NO_AUTH},
{"noauth", CallRouteQoS::NO_AUTH},
{"Cancelled", CallRouteQoS::CANCELLED},
{"Congestion", CallRouteQoS::CONGESTION},
{"congestion", CallRouteQoS::CONGESTION},
{"Unsupported Media Type", CallRouteQoS::NO_MEDIA},
{"nomedia", CallRouteQoS::NO_MEDIA},
{0,0}
};
static TokenDict s_callCounterQueries[] = {
{"incomingCalls", CallMonitor::INCOMING_CALLS},
{"outgoingCalls", CallMonitor::OUTGOING_CALLS},
{"qosDirectionsCount", CallMonitor::ROUTES_COUNT},
{0,0}
};
static TokenDict s_activeCallInfo[] = {
{"activeCallsCount", ActiveCallsInfo::COUNT},
{"callEntryID", ActiveCallsInfo::ID},
{"callEntryIndex", ActiveCallsInfo::INDEX},
{"callEntryID", ActiveCallsInfo::ID},
{"callEntryStatus", ActiveCallsInfo::STATUS},
{"callEntryCaller", ActiveCallsInfo::CALLER},
{"callEntryCalled", ActiveCallsInfo::CALLED},
{"callEntryPeerChan", ActiveCallsInfo::PEER},
{"callEntryDuration", ActiveCallsInfo::DURATION},
{0,0}
};
TokenDict TrunkInfo::s_trunkInfo[] = {
{"trunksCount", TrunkInfo::COUNT},
{"trunkIndex", TrunkInfo::INDEX},
{"trunkID", TrunkInfo::ID},
{"trunkType", TrunkInfo::TYPE},
{"trunkCircuitCount", TrunkInfo::CIRCUITS},
{"trunkCurrentCallsCount", TrunkInfo::CALLS},
{"trunkDownAlarms", TrunkInfo::ALARMS_COUNT},
{"trunkCircuitsLocked", TrunkInfo::LOCKED},
{"trunkCircuitsIdle", TrunkInfo::IDLE},
{0,0}
};
TokenDict TrunkInfo::s_trunkStatus[] = {
{"module", TrunkInfo::SKIP},
{"trunk", TrunkInfo::ID},
{"type", TrunkInfo::TYPE},
{"circuits", TrunkInfo::CIRCUITS},
{"calls", TrunkInfo::CALLS},
{"status", TrunkInfo::STATUS},
{"locked", TrunkInfo::LOCKED},
{"idle", TrunkInfo::IDLE},
{0,0}
};
static TokenDict s_accountInfo[] = {
{"accountsCount", AccountsInfo::COUNT},
{"accountIndex", AccountsInfo::INDEX},
{"accountID", AccountsInfo::ID},
{"accountStatus", AccountsInfo::STATUS},
{"accountProtocol", AccountsInfo::PROTO},
{"accountUsername", AccountsInfo::USERNAME},
{0,0}
};
static TokenDict s_engineQuery[] = {
{"plugins", EngineInfo::ENGINE_PLUGINS},
{"handlers", EngineInfo::ENGINE_HANDLERS},
{"messages", EngineInfo::ENGINE_MESSAGES},
{"msgMaxQueued", EngineInfo::ENGINE_MAXQUEUED},
{"msgLastSecond", EngineInfo::ENGINE_MSGRATE},
{"msgMaxPerSecond", EngineInfo::ENGINE_MAXMSGRATE},
{"msgEnqueued", EngineInfo::ENGINE_MSGENQUEUED},
{"msgDequeued", EngineInfo::ENGINE_MSGDEQUEUED},
{"msgDispatched", EngineInfo::ENGINE_MSGDISPATCHED},
{"threads", EngineInfo::ENGINE_THREADS},
{"workers", EngineInfo::ENGINE_WORKERS},
{"mutexes", EngineInfo::ENGINE_MUTEXES},
{"locks", EngineInfo::ENGINE_LOCKS},
{"semaphores", EngineInfo::ENGINE_SEMAPHORES},
{"waitingSemaphores", EngineInfo::ENGINE_WAITING},
{"acceptStatus", EngineInfo::ENGINE_CALL_ACCEPT},
// node info
{"runAttempt", EngineInfo::ENGINE_RUNATTEMPT},
{"name", EngineInfo::ENGINE_NODENAME},
{"state", EngineInfo::ENGINE_STATE},
{"unexpectedRestart", EngineInfo::ENGINE_UNEX_RESTART},
{0,0}
};
TokenDict EngineInfo::s_engineInfo[] = {
{"type", EngineInfo::ENGINE_TYPE},
{"plugins", EngineInfo::ENGINE_PLUGINS},
{"handlers", EngineInfo::ENGINE_HANDLERS},
{"messages", EngineInfo::ENGINE_MESSAGES},
{"maxqueue", EngineInfo::ENGINE_MAXQUEUED},
{"messagerate", EngineInfo::ENGINE_MSGRATE},
{"maxmsgrate", EngineInfo::ENGINE_MAXMSGRATE},
{"enqueued", EngineInfo::ENGINE_MSGENQUEUED},
{"dequeued", EngineInfo::ENGINE_MSGDEQUEUED},
{"dispatched", EngineInfo::ENGINE_MSGDISPATCHED},
{"threads", EngineInfo::ENGINE_THREADS},
{"workers", EngineInfo::ENGINE_WORKERS},
{"mutexes", EngineInfo::ENGINE_MUTEXES},
{"locks", EngineInfo::ENGINE_LOCKS},
{"semaphores", EngineInfo::ENGINE_SEMAPHORES},
{"waiting", EngineInfo::ENGINE_WAITING},
{"runattempt", EngineInfo::ENGINE_RUNATTEMPT},
{"nodename", EngineInfo::ENGINE_NODENAME},
{"acceptcalls", EngineInfo::ENGINE_CALL_ACCEPT},
{"lastsignal", EngineInfo::ENGINE_UNEX_RESTART},
{0,0}
};
TokenDict ModuleInfo::s_moduleInfo[] = {
{"name", ModuleInfo::MODULE_NAME},
{"type", ModuleInfo::MODULE_TYPE},
{"format", ModuleInfo::MODULE_FORMAT},
{0,0}
};
static TokenDict s_moduleQuery[] = {
{"moduleCount", ModuleInfo::COUNT},
{"moduleIndex", ModuleInfo::INDEX},
{"moduleName", ModuleInfo::MODULE_NAME},
{"moduleType", ModuleInfo::MODULE_TYPE},
{"moduleExtra", ModuleInfo::MODULE_INFO},
{0,0}
};
TokenDict DatabaseInfo::s_databaseInfo[] = {
{"conns", DatabaseInfo::Connections},
{"failed", DatabaseInfo::FailedConns},
{0,0}
};
static TokenDict s_databaseQuery[] = {
{"successfulConnections", DatabaseInfo::Connections},
{"failedConnections", DatabaseInfo::FailedConns},
{"databaseCount", DatabaseInfo::Accounts},
{0,0}
};
static TokenDict s_dbAccountQueries[] = {
{"databaseIndex", DatabaseAccount::AccountIndex},
{"databaseAccount", DatabaseAccount::AccountName},
{"queriesCount", DatabaseAccount::QueriesCount},
{"failedQueries", DatabaseAccount::FailedCount},
{"errorQueries", DatabaseAccount::ErrorsCount},
{"queryExecTime", DatabaseAccount::ExecTime},
// alarms counters
{"tooManyQueriesAlarms", DatabaseAccount::TooManyAlrmCount},
{"tooManyFailedQueriesAlarms", DatabaseAccount::TooManyFailedAlrmCount},
{"tooManyErrorQueriesAlarms", DatabaseAccount::TooManyErrorAlrmCount},
{"queryExecTooLongAlarms", DatabaseAccount::ExecTooLongAlrmCount},
{"noConnectionAlarms", DatabaseAccount::NoConnAlrmCount},
// database thresholds
{"queriesCountThreshold", DatabaseAccount::MaxQueries},
{"failedQueriesThreshold", DatabaseAccount::MaxFailedQueries},
{"errorQueriesThreshold", DatabaseAccount::MaxErrorQueries},
{"queryExecTimeThreshold", DatabaseAccount::MaxExecTime},
// alarm
{"tooManyQueries", DatabaseAccount::TooManyAlrm},
{"tooManyFailedQueries", DatabaseAccount::TooManyFailedAlrm},
{"tooManyErrorQueries", DatabaseAccount::TooManyErrorAlrm},
{"queryExecTimeTooLong", DatabaseAccount::ExecTooLongAlrm},
{"noConnection", DatabaseAccount::NoConnAlrm},
{0,0}
};
static TokenDict s_dbAccountInfo[] = {
{"maxqueries", DatabaseAccount::MaxQueries},
{"maxfailed", DatabaseAccount::MaxFailedQueries},
{"maxerrors", DatabaseAccount::MaxErrorQueries},
{"maxtimeperquery", DatabaseAccount::MaxExecTime},
{"total", DatabaseAccount::TOTAL_IDX},
{"failed", DatabaseAccount::FAILED_IDX},
{"errorred", DatabaseAccount::ERROR_IDX},
{"querytime", DatabaseAccount::TIME_IDX},
{"hasconn", DatabaseAccount::CONN_IDX},
{0,-1}
};
TokenDict RTPEntry::s_rtpInfo[] = {
{"remoteip", RTPEntry::Direction},
{"noaudio", RTPEntry::NoAudio},
{"lostaudio", RTPEntry::LostAudio},
{"lostpkts", RTPEntry::PktsLost},
{"synclost", RTPEntry::SyncLost},
{"seqslost", RTPEntry::SeqLost},
{"wrongsrc", RTPEntry::WrongSRC},
{"wrongssrc", RTPEntry::WrongSSRC},
{0,0}
};
static TokenDict s_rtpQuery[] = {
{"rtpDirectionsCount", RTPEntry::Count},
{"rtpEntryIndex", RTPEntry::Index},
{"rtpDirection", RTPEntry::Direction},
{"noAudioCounter", RTPEntry::NoAudio},
{"lostAudioCounter", RTPEntry::LostAudio},
{"packetsLost", RTPEntry::PktsLost},
{"syncLost", RTPEntry::SyncLost},
{"sequenceNumberLost", RTPEntry::SeqLost},
{"wrongSRC", RTPEntry::WrongSRC},
{"wrongSSRC", RTPEntry::WrongSSRC},
{0,0}
};
static TokenDict s_sigTypes[] = {
{"ss7-mtp3", Monitor::SS7_MTP3},
{"trunk", Monitor::TRUNK},
{"isdn-q921", Monitor::ISDN},
{0,0}
};
static TokenDict s_sipNotifs[] = {
{"transactionsTimedOut", Monitor::TransactTimedOut},
{"failedAuths", Monitor::FailedAuths},
{"byesTimedOut", Monitor::ByesTimedOut},
{"gatewayTimeout", Monitor::GWTimeout},
{"gatewayUp", Monitor::GWUp},
{0,0}
};
static TokenDict s_mgcpNotifs[] = {
{"mgcpTransactionsTimedOut", Monitor::TransactTimedOut},
{"deleteTransactionsTimedOut", Monitor::DeletesTimedOut},
{"mgcpGatewayTimedOut", Monitor::GWTimeout},
{"mgcpGatewayUp", Monitor::GWUp},
{0,0}
};
static TokenDict s_sigNotifs[] = {
{"trunkDown", Monitor::TrunkDown},
{"trunkUp", Monitor::TrunkUp},
{"linksetDown", Monitor::LinksetDown},
{"linksetUp", Monitor::LinksetUp},
{"linkUp", Monitor::LinkUp},
{"linkDown", Monitor::LinkDown},
{"linkUp", Monitor::LinkUp},
{"isdnQ921Down", Monitor::IsdnQ921Down},
{"isdnQ921Up", Monitor::IsdnQ921Up},
{0,0}
};
static TokenDict s_cardInfo[] = {
{"interfaceDown", Monitor::InterfaceDown},
{"interfaceUp", Monitor::InterfaceUp},
{0,0}
};
static TokenDict s_cardNotifs[] = {
{"interfaceDown", Monitor::InterfaceDown},
{"interfaceUp", Monitor::InterfaceUp},
{0,0}
};
TokenDict LinksetInfo::s_linksetInfo[] = {
{"linksetCount", LinksetInfo::COUNT},
{"linksetIndex", LinksetInfo::INDEX},
{"linksetID", LinksetInfo::ID},
{"linksetType", LinksetInfo::TYPE},
{"linksetStatus", LinksetInfo::STATUS},
{"linksetDownAlarms", LinksetInfo::ALARMS_COUNT},
{0,0}
};
TokenDict LinksetInfo::s_linksetStatus[] = {
{"module", LinksetInfo::SKIP},
{"component", LinksetInfo::ID},
{"type", LinksetInfo::TYPE},
{"status", LinksetInfo::STATUS},
{0,0}
};
TokenDict LinkInfo::s_linkInfo[] = {
{"linkCount", LinkInfo::COUNT},
{"linkIndex", LinkInfo::INDEX},
{"linkID", LinkInfo::ID},
{"linkType", LinkInfo::TYPE},
{"linkStatus", LinkInfo::STATUS},
{"linkDownAlarms", LinkInfo::ALARMS_COUNT},
{"linkUptime", LinkInfo::UPTIME},
{0,0}
};
TokenDict InterfaceInfo::s_ifacesInfo[] = {
{"interfacesCount", InterfaceInfo::COUNT},
{"interfaceIndex", InterfaceInfo::INDEX},
{"interfaceID", InterfaceInfo::ID},
{"interfaceStatus", InterfaceInfo::STATUS},
{"interfaceDownAlarms", InterfaceInfo::ALARMS_COUNT},
{0,0}
};
String DatabaseInfo::s_dbParam = "database.";
String DatabaseInfo::s_totalParam = "total.";
String DatabaseInfo::s_failedParam = "failed.";
String DatabaseInfo::s_errorParam = "errorred.";
String DatabaseInfo::s_hasConnParam = "hasconn.";
String DatabaseInfo::s_timeParam = "querytime.";
INIT_PLUGIN(Monitor);
UNLOAD_PLUGIN(unloadNow)
{
if (unloadNow && !__plugin.unload())
return false;
return true;
}
/**
* Class MsgUpdateHandler
* Class for handling a "module.update" message
*/
class MsgUpdateHandler : public MessageHandler
{
public:
inline MsgUpdateHandler(unsigned int priority = 100)
: MessageHandler("module.update",priority,__plugin.name())
{ }
virtual ~MsgUpdateHandler()
{ }
virtual bool received(Message& msg);
};
/**
* Class SnmpMsgHandler
* Class for handling a "monitor.query" message, message used for obtaining information from the monitor
*/
class SnmpMsgHandler : public MessageHandler
{
public:
inline SnmpMsgHandler(unsigned int priority = 100)
: MessageHandler("monitor.query",priority,__plugin.name())
{ }
virtual ~SnmpMsgHandler()
{ }
virtual bool received(Message& msg);
};
/**
* Class HangupHandler
* Handler for "chan.hangup" message"
*/
class HangupHandler : public MessageHandler
{
public:
inline HangupHandler(unsigned int priority = 100)
: MessageHandler("chan.hangup",priority,__plugin.name())
{ }
virtual ~HangupHandler()
{ }
virtual bool received(Message& msg);
};
/**
* Class EngineStartHandler
* Handler for "engine.start" message
*/
class EngineStartHandler : public MessageHandler
{
public:
inline EngineStartHandler(unsigned int priority = 100)
: MessageHandler("engine.start",priority,__plugin.name())
{ }
virtual ~EngineStartHandler()
{ }
virtual bool received(Message& msg);
};
/**
* Class AuthHandler
* Handler for a "user.auth" message. It counts the number of authentication requests
*/
class AuthHandler : public MessageHandler
{
public:
inline AuthHandler()
: MessageHandler("user.auth",1,__plugin.name()),
m_count(0)
{ }
virtual ~AuthHandler()
{ }
virtual bool received(Message& msg);
// return the number of authentication requests
inline unsigned int getCount()
{ return m_count; }
private:
unsigned int m_count;
};
/**
* Class RegisterHandler
* Handler for a "user.register" message. It counts the number of register requests
*/
class RegisterHandler : public MessageHandler
{
public:
inline RegisterHandler()
: MessageHandler("user.register",1,__plugin.name()),
m_count(0)
{ }
virtual ~RegisterHandler()
{ }
virtual bool received(Message& msg);
// return the count
inline unsigned int getCount()
{ return m_count; }
private:
unsigned int m_count;
};
// helper function to get rid of new line characters
static void cutNewLine(String& line) {
if (line.endsWith("\n"))
line = line.substr(0,line.length() - 1);
if (line.endsWith("\r"))
line = line.substr(0,line.length() - 1);
}
// callback for engine alarm hook
static void alarmCallback(const char* message, int level, const char* component, const char* info)
{
if (TelEngine::null(component) || TelEngine::null(message))
return;
const char* lvl = debugLevelName(level);
if (TelEngine::null(lvl))
return;
TempObjectCounter cnt(__plugin.objectsCounter());
Message* msg = new Message("module.update");
msg->addParam("module",__plugin.name());
msg->addParam("level",String(level));
msg->addParam("from",component,false);
msg->addParam("text",message,false);
msg->addParam("info",info,false);
Engine::enqueue(msg);
if ((s_alarmThreshold >= DebugFail) && (level <= s_alarmThreshold)) {
msg = new Message("monitor.notify",0,true);
msg->addParam("notify","genericAlarm");
msg->addParam("notify.0","alarmSource");
msg->addParam("value.0",component);
msg->addParam("notify.1","alarmLevel");
msg->addParam("value.1",lvl);
msg->addParam("notify.2","alarmText");
msg->addParam("value.2",message);
if (!TelEngine::null(info)) {
msg->addParam("notify.3","alarmInfo");
msg->addParam("value.3",info);
}
Engine::enqueue(msg);
}
}
/**
* MsgUpdateHandler
*/
bool MsgUpdateHandler::received(Message& msg)
{
DDebug(__plugin.name(),DebugAll,"MsgUpdateHandler::received()");
__plugin.update(msg);
return true;
}
/**
* SnmpMsgHandler
*/
bool SnmpMsgHandler::received(Message& msg)
{
DDebug(__plugin.name(),DebugAll,"SnmpMsgHandler::received()");
return __plugin.solveQuery(msg);
}
/**
* HangupHandler
*/
bool HangupHandler::received(Message& msg)
{
DDebug(__plugin.name(),DebugAll,"HangupHandler::received()");
String status = msg.getValue("status","");
String address = msg.getValue("address","");
int cause = msg.getIntValue("cause_sip",0);
if (status == "outgoing" && cause && !address.null())
__plugin.handleChanHangup(address,cause);
if (status == "ringing" && !address.null())
__plugin.verifyGateway(address);
return false;
}
/**
* EngineStartHandler
*/
bool EngineStartHandler::received(Message& msg)
{
DDebug(__plugin.name(),DebugAll,"EngineStartHandler::received()");
s_yateRun = Engine::runParams().getIntValue("runattempt",0);
if (s_yateRun >= s_yateRunAlarm && s_yateRunAlarm >= 1) {
String notif = lookup(EngineInfo::ENGINE_RUNATTEMPT,s_engineQuery,"");
if (!notif.null())
__plugin.sendTrap(notif,String(s_yateRun));
}
int lastsignal = Engine::runParams().getIntValue(YSTRING("lastsignal"),0);
if (lastsignal > 0) {
String notif = lookup(EngineInfo::ENGINE_UNEX_RESTART,s_engineQuery,"");
if (!notif.null())
__plugin.sendTrap(notif,String(lastsignal));
}
return false;
};
/**
* AuthHandler
*/
bool AuthHandler::received(Message& msg)
{
String user = msg.getValue("username","");
if (!user.null())
m_count++;
return false;
}
/**
* RegisterHandler
*/
bool RegisterHandler::received(Message& msg)
{
m_count++;
return false;
}
/**
* Cache
*/
Cache::~Cache()
{
discard();
}
bool Cache::load()
{
return false;
}
// discard cached data
void Cache::discard()
{
DDebug(&__plugin,DebugInfo,"Cache::discard() [%p] - dropping cached data",this);
Lock l(this);
m_reload = true;
m_table.clear();
}
String Cache::getInfo(const String& query, unsigned int& index, TokenDict* dict)
{
DDebug(&__plugin,DebugAll,"Cache::getInfo(query='%s',index='%d') [%p]",query.c_str(),index,this);
// if we have data, check if it is still valid
if (isExpired())
discard();
else
// if the data has not yet expired, update the expire time
updateExpire();
String retStr;
// if the is no data available, obtain it from an engine.status message
if (m_reload && !load())
return retStr;
Lock l(this);
// lookup the type of the query, and if it's of type COUNT, return the number of entries
int type = lookup(query,dict,0);
if (type == COUNT) {
retStr += m_table.count();
return retStr;
}
// if it's not of type COUNT, check if the requested index is within the range of the table
if (index < 1 || index > m_table.count())
return retStr;
// get the entry of the given index
NamedList* nl = static_cast<NamedList*>(m_table[index - 1]);
if (!nl)
return retStr;
// get the result
if (type == INDEX) {
retStr += index;
return retStr;
}
retStr = nl->getValue(query,"");
if (retStr.null())
retStr = "no info";
return retStr;
}
/**
* ActiveCallInfo
*/
// match bill ids to find a call's peer
String ActiveCallsInfo::checkPeers(const String& billID, const String& id)
{
DDebug(&__plugin,DebugAll,"ActiveCallsInfo::checkPeers('%s','%s')",billID.c_str(),id.c_str());
if (billID.null())
return id;
String retPeers;
for(ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
NamedList* nl = static_cast<NamedList*>(o->get());
if (!nl)
continue;
String otherBillID = nl->getValue("billId","");
String peers = nl->getValue(lookup(PEER,s_activeCallInfo,0),"");
if (billID == otherBillID) {
String otherID = nl->getValue(lookup(ID,s_activeCallInfo,0),"");
peers.append(id,";");
retPeers.append(otherID,";");
}
nl->setParam(lookup(PEER,s_activeCallInfo,0),peers);
}
return retPeers;
}
// load data into the cache
bool ActiveCallsInfo::load()
{
DDebug(&__plugin,DebugInfo,"ActiveCallsInfo::load() [%p] - loading data",this);
// emit an engine.status message
Message m("engine.status");
m.addParam("module","cdrbuild");
Engine::dispatch(m);
String& status = m.retValue();
if (TelEngine::null(status))
return false;
Lock l(this);
m_table.clear();
int pos = status.rfind(';');
if (pos < 0)
return false;
status = status.substr(pos + 1);
ObjList* calls = status.split(',');
for (ObjList* o = calls->skipNull(); o; o = o->skipNext()) {
String* callInfo = static_cast<String*>(o->get());
if ( *callInfo == "\r\n")
continue;
if (pos > -1) {
int pos = callInfo->find("=");
NamedList* nl = new NamedList("");
String id = callInfo->substr(0,pos);
callInfo->startSkip(String(id + "="));
nl->setParam(lookup(ID,s_activeCallInfo,0),id);
int i = 0;
String peers;
while (i < 5) {
pos = callInfo->find("|");
if (pos < -1)
break;
String val = callInfo->substr(0,pos);
callInfo->startSkip(String(val + "|"),false);
int p = -1;
switch (i) {
case 0:
p = val.find("=");
if (p > -1)
val = val.substr(p+1);
nl->setParam(lookup(STATUS,s_activeCallInfo,0),val);
break;
case 1:
val = ( val.null() ? "no info" : val);
nl->setParam(lookup(CALLER,s_activeCallInfo,0),val);
break;
case 2:
val = ( val.null() ? "no info" : val);
nl->setParam(lookup(CALLED,s_activeCallInfo,0),val);
break;
case 3:
peers = checkPeers(val,nl->getValue(lookup(ID,s_activeCallInfo,0),""));
nl->setParam("billId",val);
nl->setParam(lookup(PEER,s_activeCallInfo,0),peers);
break;
case 4:
cutNewLine(val);
val = ( val.null() ? "no info" : val);
nl->setParam(lookup(DURATION,s_activeCallInfo,0),val);
break;
default:
break;
}
i++;
}
m_table.append(nl);
}
}
TelEngine::destruct(calls);
updateExpire();
return true;
}
/**
* InterfaceInfo
*/
// reimplementation of the Cache::discard() function, only resets the status information
void SigInfo::discard()
{
if (!m_dictionary)
return;
DDebug(&__plugin,DebugAll,"SigInfo::discard() [%p] - dropping cached data",this);
for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
NamedList* nl = static_cast<NamedList*>(o->get());
nl->setParam(lookup(STATUS,m_dictionary,""),"unknown");
}
m_reload = true;
}
// STUB
bool SigInfo::load()
{
DDebug(&__plugin,DebugWarn,"SigInfo::load() [%p] - STUB",this);
return true;
}
// increase the counter for number of alarms sent
void SigInfo::updateAlarmCounter(const String& name)
{
if (!m_dictionary)
return;
DDebug(&__plugin,DebugAll,"SigInfo::updateAlarmCounter('%s') [%p]",name.c_str(),this);
NamedList* nl = static_cast<NamedList*>(m_table[name]);
if (!nl) {
load();
nl = static_cast<NamedList*>(m_table[name]);
}
if (nl) {
String param = lookup(ALARMS_COUNT,m_dictionary,"");
if (param.null())
return;
int val = nl->getIntValue(param,0);
val++;
nl->setParam(param,String(val));
}
}
/**
* InterfaceInfo
*/
// parse interface information and store it in the cache
bool InterfaceInfo::load()
{
DDebug(&__plugin,DebugAll,"InterfaceInfo::load() [%p] - updating internal data",this);
Message m("engine.status");
m.addParam("module","sig ifaces");
Engine::dispatch(m);
String& status = m.retValue();
if (!TelEngine::null(status)) {
cutNewLine(status);
ObjList* parts = status.split(';');
if (!(parts && parts->count() > 2)) {
TelEngine::destruct(parts);
return true;
}
String ifaces = static_cast<String*>(parts->at(2));
if (ifaces.null()) {
TelEngine::destruct(parts);
return true;
}
Lock l(this);
ObjList* list = ifaces.split(',');
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
String iface = static_cast<String*>(o->get());
String name, status;
iface.extractTo("=",name).extractTo("|",status);
if (name.null())
continue;
NamedList* nl = static_cast<NamedList*>(m_table[name]);
if (!nl) {
nl = new NamedList(name);
nl->setParam(lookup(ID,s_ifacesInfo,""),name);
nl->setParam(lookup(STATUS,s_ifacesInfo,""),status);
m_table.append(nl);
}
else
nl->setParam(lookup(STATUS,s_ifacesInfo,""),status);
if (!nl->getParam(lookup(ALARMS_COUNT,s_ifacesInfo,"")))
nl->setParam(lookup(ALARMS_COUNT,s_ifacesInfo,""),"0");
}
TelEngine::destruct(list);
TelEngine::destruct(parts);
}
updateExpire();
return true;
}
/**
* LinkInfo
*/
// parse link information
bool LinkInfo::load()
{
DDebug(&__plugin,DebugAll,"LinkInfo::load() [%p] - loading data",this);
Message m("engine.status");
m.addParam("module","sig links");
Engine::dispatch(m);
String& status = m.retValue();
if (!TelEngine::null(status)) {
cutNewLine(status);
ObjList* parts = status.split(';');
if (!(parts && parts->count() > 2)) {
TelEngine::destruct(parts);
return true;
}
String links = static_cast<String*>(parts->at(2));
if (links.null()) {
TelEngine::destruct(parts);
return true;
}
Lock l(this);
ObjList* list = links.split(',');
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
String link = static_cast<String*>(o->get());
String name,type,status;
int uptime = 0;
link.extractTo("=",name).extractTo("|",type).
extractTo("|",status).extractTo("|",uptime);
if (name.null() || type.null())
continue;
NamedList* nl = static_cast<NamedList*>(m_table[name]);
if (!nl) {
nl = new NamedList(name);
nl->setParam(lookup(ID,s_linkInfo,""),name);
nl->setParam(lookup(TYPE,s_linkInfo,""),type);
nl->setParam(lookup(STATUS,s_linkInfo,""),status);
nl->setParam(lookup(UPTIME,s_linkInfo,""),String(uptime));
m_table.append(nl);
}
else {
nl->setParam(lookup(STATUS,s_linkInfo,""),status);
nl->setParam(lookup(UPTIME,s_linkInfo,""),String(uptime));
}
if (!nl->getParam(lookup(ALARMS_COUNT,s_linkInfo,"")))
nl->setParam(lookup(ALARMS_COUNT,s_linkInfo,""),"0");
if (!nl->getParam(lookup(UPTIME,s_linkInfo,"")))
nl->setParam(lookup(UPTIME,s_linkInfo,""),"0");
}
TelEngine::destruct(list);
TelEngine::destruct(parts);
}
updateExpire();
return true;
}
/**
* LinksetInfo
*/
// parse linkset information
bool LinksetInfo::load()
{
DDebug(&__plugin,DebugAll,"LinksetInfo::load() [%p] - loading data",this);
Message m("engine.command");
m.addParam("partial","status sig ");
m.addParam("partline","status sig");
if (!Engine::dispatch(m))
return false;
String& status = m.retValue();
if (TelEngine::null(status))
return false;
Lock l(this);
ObjList* links = status.split('\t');
for (ObjList* o = links->skipNull(); o; o = o->skipNext()) {
String* link = static_cast<String*>(o->get());
if (*link == "links" || *link == "ifaces")
continue;
Message msg("engine.status");
msg.addParam("module",String("sig " + *link));
Engine::dispatch(msg);
String& linkInfo = msg.retValue();
if (linkInfo.null())
continue;
NamedList* nl = static_cast<NamedList*>(m_table[*link]);
if (nl)
parseLinksetInfo(linkInfo,*link,nl);
else {
nl = parseLinksetInfo(linkInfo,*link);
if (nl)
m_table.append(nl);
}
}
TelEngine::destruct(links);
updateExpire();
return true;
}
// parse the information about a single linkset
NamedList* LinksetInfo::parseLinksetInfo(String& info,const String& link, NamedList* infoFill)
{
cutNewLine(info);
DDebug(&__plugin,DebugAll,"LinksetInfo::parseLinkInfo(info='%s',link='%s', infoFill='%p') [%p]",info.c_str(),link.c_str(),infoFill,this);
NamedList* nl = (infoFill ? infoFill : new NamedList(link));
ObjList* parts = info.split(';',false);
for (ObjList* obj = parts->skipNull(); obj; obj = obj->skipNext()) {
String* infoPart = static_cast<String*>(obj->get());
if (TelEngine::null(infoPart))
continue;
ObjList* params = infoPart->split(',',false);
for (ObjList* o = params->skipNull(); o; o = o->skipNext()) {
String* param = static_cast<String*>(o->get());
int pos = param->find("=");
if (pos < 0)
continue;
String nameParam = param->substr(0,pos);
String valParam = param->substr(pos + 1);
int type = lookup(nameParam,s_linksetStatus,0);
if (type > 0) {
if (type == TYPE && (valParam.null() || valParam != "ss7-mtp3")) {
TelEngine::destruct(params);
TelEngine::destruct(nl);
TelEngine::destruct(parts);
return 0;
}
nl->setParam(lookup(type,s_linksetInfo,""),valParam);
}
}
TelEngine::destruct(params);
}
NamedString* linksetId = nl->getParam(lookup(ID,s_linksetInfo));
NamedString* typeStr = nl->getParam(lookup(TYPE,s_linksetInfo));
if (TelEngine::null(linksetId) || TelEngine::null(typeStr)) {
TelEngine::destruct(parts);
TelEngine::destruct(nl);
return 0;
}
TelEngine::destruct(parts);
if (!nl->getParam(lookup(ALARMS_COUNT,s_linksetInfo,"")))
nl->setParam(lookup(ALARMS_COUNT,s_linksetInfo,""),"0");
return nl;
}
/**
* TrunkInfo
*/
// reset trunk information
void TrunkInfo::discard()
{
DDebug(&__plugin,DebugAll,"TrunkInfo::discard() [%p] - dropping cached data",this);
for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
NamedList* nl = static_cast<NamedList*>(o->get());
nl->setParam(lookup(TYPE,s_trunkInfo,""),"");
nl->setParam(lookup(CIRCUITS,s_trunkInfo,""),"0");
nl->setParam(lookup(CALLS,s_trunkInfo,""),"0");
nl->setParam(lookup(LOCKED,s_trunkInfo,""),"0");
nl->setParam(lookup(IDLE,s_trunkInfo,""),"0");
}
m_reload = true;
}
// parse and load trunk information
bool TrunkInfo::load()
{
DDebug(&__plugin,DebugAll,"TrunkInfo::load() [%p] - loading data",this);
Message m("engine.command");
m.addParam("partial","status sig ");
m.addParam("partline","status sig");
if (!Engine::dispatch(m))
return false;
String& status = m.retValue();
if (TelEngine::null(status))
return false;
Lock l(this);
ObjList* trunks = status.split('\t');
for (ObjList* o = trunks->skipNull(); o; o = o->skipNext()) {
String* trunk = static_cast<String*>(o->get());
if ((*trunk) == "links" || (*trunk) == "ifaces")
continue;
Message msg("engine.status");
msg.addParam("module",String("sig " + *trunk));
Engine::dispatch(msg);
String& trunkInfo = msg.retValue();
if (trunkInfo.null())
continue;
NamedList* nl = static_cast<NamedList*>(m_table[*trunk]);
if (nl)
parseTrunkInfo(trunkInfo,*trunk,nl);
else {
nl = parseTrunkInfo(trunkInfo,*trunk);
if (nl)
m_table.append(nl);
}
}
TelEngine::destruct(trunks);
// update the expire time
updateExpire();
return true;
}
// parse the information for a single trunk
NamedList* TrunkInfo::parseTrunkInfo(String& info, const String& trunk, NamedList* infoFill)
{
cutNewLine(info);
DDebug(&__plugin,DebugAll,"TrunkInfo::parseTrunkInfo(info='%s',trunk='%s', infoFill='%p') [%p]",info.c_str(),trunk.c_str(),infoFill,this);
NamedList* nl = (infoFill ? infoFill : new NamedList(trunk));
ObjList* parts = info.split(';',false);
for (ObjList* obj = parts->skipNull(); obj; obj = obj->skipNext()) {
String* infoPart = static_cast<String*>(obj->get());
if (TelEngine::null(infoPart))
continue;
ObjList* params = infoPart->split(',',false);
for (ObjList* o = params->skipNull(); o; o = o->skipNext()) {
String* param = static_cast<String*>(o->get());
int pos = param->find("=");
if (pos < 0) {
TelEngine::destruct(params);
continue;
}
String nameParam = param->substr(0,pos);
String valParam = param->substr(pos + 1);
int type = lookup(nameParam,s_trunkStatus,0);
if (type > 0)
nl->setParam(lookup(type,s_trunkInfo,""),valParam);
}
TelEngine::destruct(params);
}
// check that it's indeed a trunk
NamedString* trunkId = nl->getParam(lookup(ID,s_trunkInfo));
if (TelEngine::null(trunkId)) {
TelEngine::destruct(parts);
TelEngine::destruct(nl);
return 0;
}
TelEngine::destruct(parts);
if (!nl->getParam(lookup(ALARMS_COUNT,s_trunkInfo,"")))
nl->setParam(lookup(ALARMS_COUNT,s_trunkInfo,""),"0");
return nl;
}
/**
* AccountsInfo
*/
// parse and store accounts information
bool AccountsInfo::load()
{
DDebug(&__plugin,DebugAll,"AccountsInfo::load() [%p] - loading data",this);
String modules[] = {"sip","h323","iax","jabberclient"};
int i = 0;
while (i < 4) {
Message m("engine.status");
m.setParam("module",modules[i] + " accounts");
Engine::dispatch(m);
String& status = m.retValue();
i++;
if (TelEngine::null(status))
continue;
cutNewLine(status);
//find protocol
String protoParam = "protocol=";
int pos = status.find(protoParam);
if (pos < 0)
continue;
int auxPos = status.find(",",pos);
if (auxPos < pos + (int)protoParam.length())
continue;
String proto = status.substr(pos + protoParam.length(),auxPos - (pos + protoParam.length()));
pos = status.rfind(';');
if (pos < 0)
continue;
status = status.substr(pos + 1);
Lock l(this);
ObjList* accounts = status.split(',',false);
for (ObjList* o = accounts->skipNull(); o; o = o->skipNext()) {
String* account = static_cast<String*>(o->get());
int pos1 = account->find("=");
int pos2 = account->find("|");
if (pos1 < 0 || pos2 < 0)
continue;
String name = account->substr(0,pos1);
String username = account->substr(pos1 + 1,pos2 - pos1 -1);
String status = account->substr(pos2 + 1);
if (name.null())
continue;
NamedList* nl = new NamedList("");
nl->setParam(lookup(ID,s_accountInfo,""),name);
nl->setParam(lookup(USERNAME,s_accountInfo,""),username);
nl->setParam(lookup(STATUS,s_accountInfo,""),status);
nl->setParam(lookup(PROTO,s_accountInfo,""),proto);
m_table.append(nl);
}
TelEngine::destruct(accounts);
l.drop();
}
updateExpire();
return true;
}
/**
* EngineInfo - engine status information cache
*/
// reimplemented from Cache; obtain the result for a engine query
String EngineInfo::getInfo(const String query, unsigned int index, TokenDict* dict)
{
DDebug(&__plugin,DebugAll,"EngineInfo::getInfo(%s %d) [%p]",query.c_str(),index,this);
// if we have data, check if it is still valid
if (isExpired())
discard();
else
// if the data has not yet expired, update the expire time
updateExpire();
String retStr;
// if the is no data available, obtain it from an engine.status message
if (m_reload && !load())
return retStr;
Lock l(this);
int type = lookup(query,s_engineQuery,0);
if (!type)
return retStr;
if (index > 1)
return retStr;
NamedList* nl = static_cast<NamedList*>(m_table[index]);
if (!nl)
return retStr;
if (type == ENGINE_STATE)
return s_nodeState;
retStr = nl->getValue(query,"");
if (retStr.null())
retStr = "no info";
return retStr;
}
// load data into the cache
bool EngineInfo::load()
{
DDebug(&__plugin,DebugInfo,"EngineInfo::load() [%p] - loading data",this);
// emit an engine.status message
Message m("engine.status");
m.setParam("module","engine");
Engine::dispatch(m);
String& status = m.retValue();
if (TelEngine::null(status))
return false;
cutNewLine(status);
Lock l(this);
m_table.clear();
NamedList* nl = new NamedList("");
ObjList* params = status.split(';');
for(ObjList* o = params->skipNull(); o; o = o->skipNext()) {
String* strVal = static_cast<String*>(o->get());
ObjList* l = strVal->split(',');
for (ObjList* j = l->skipNull(); j; j = j->skipNext()) {
String* str = static_cast<String*>(j->get());
int pos = str->find("=");
if (pos < 0)
continue;
String param = str->substr(0,pos);
String value = str->substr(pos + 1);
int type = lookup(param,s_engineInfo,0);
if (!type)
continue;
nl->setParam(lookup(type,s_engineQuery,""),value);
}
TelEngine::destruct(l);
}
m_table.append(nl);
TelEngine::destruct(params);
updateExpire();
return true;
}
/**
* ModuleInfo
*/
// load data into the cache
bool ModuleInfo::load()
{
DDebug(&__plugin,DebugInfo,"ModuleInfo::load() [%p] - loading data",this);
// emit an engine.status message
Message m("engine.status");
m.setParam("details",String::boolText(false));
Engine::dispatch(m);
String& status = m.retValue();
if (TelEngine::null(status))
return false;
Lock l(this);
m_table.clear();
// split the info into lines
ObjList* lines = status.split('\n',false);
for (ObjList* o = lines->skipNull(); o; o = o->skipNext()) {
String* line = static_cast<String*>(o->get());
if (!line)
continue;
cutNewLine(*line);
ObjList* parts = line->split(';');
NamedList* nl = new NamedList("");
for (ObjList* p = parts->skipNull(); p; p = p->skipNext()) {
String* str = static_cast<String*>(p->get());
ObjList* paramVal = str->split(',');
String info = "";
for (ObjList* l = paramVal->skipNull(); l; l = l->skipNext()) {
String* pair = static_cast<String*>(l->get());
int pos = pair->find("=");
if (pos < 0)
continue;
String param = pair->substr(0,pos);
String value = pair->substr(pos + 1);
int type = lookup(param,s_moduleInfo,0);
if (!type) {
info += (info.null() ? pair : String("," + *pair));
continue;
}
nl->setParam(lookup(type,s_moduleQuery,""),value);
}
nl->setParam(lookup(MODULE_INFO,s_moduleQuery,""),info);
TelEngine::destruct(paramVal);
}
TelEngine::destruct(parts);
if ( String("engine") == nl->getValue(lookup(MODULE_NAME,s_moduleQuery,""),"")) {
TelEngine::destruct(nl);
continue;
}
m_table.append(nl);
}
TelEngine::destruct(lines);
updateExpire();
return true;
}
/**
* DatabaseAccount - an object holding information about a single monitored database account
*/
// configure a database account for monitoring and initialize internal data
DatabaseAccount::DatabaseAccount(const NamedList* cfg)
: m_resetTime(0), m_resetInterval(3600),
m_isCurrent(true)
{
if (cfg) {
Debug(&__plugin,DebugAll,"DatabaseAccount('%s') created for monitoring [%p]",cfg->c_str(),this);
m_name = cfg->c_str();
m_alarms = 0;
for (int i = 0; i < ExecTime; i++) {
m_dbCounters[i] = 0;
m_prevDbCounters[i] = 0;
}
for (int i = 0; i <= NoConnAlrmCount - TooManyAlrmCount; i++)
m_alarmCounters[i] = 0;
updateConfig(cfg);
m_resetTime = Time::secNow() + m_resetInterval;
}
m_isCurrent = true;
}
// update internal data from a message received from the sql modules
void DatabaseAccount::update(const NamedList& info)
{
XDebug(&__plugin,DebugAll,"DatabaseAccount::update() [%p]",this);
for (unsigned int i = 0; i < info.count(); i++) {
NamedString* ns = info.getParam(i);
if (!(ns && *ns))
continue;
int type = lookup(ns->name(),s_dbAccountInfo,-1);
if (type < 0)
continue;
u_int16_t alarm = TOTAL_ALARM << (type);
if (type <= TIME_IDX) {
m_dbCounters[type] = ns->toInteger();
if ((type != TIME_IDX) && (m_dbCounters[type] - m_prevDbCounters[type] >= m_thresholds[type]) && ((m_alarms & alarm) == 0)) {
m_alarms |= alarm;
m_alarmCounters[type]++;
__plugin.sendTrap(lookup(TooManyAlrm + type,s_dbAccountQueries,""),toString(),index());
}
}
if (type == CONN_IDX) {
if (!ns->toBoolean()) {
if ((m_alarms & alarm) == 0) {
m_alarmCounters[CONN_IDX]++;
m_alarms |= alarm;
__plugin.sendTrap(lookup(NoConnAlrm,s_dbAccountQueries,""),toString(),index());
}
}
else
m_alarms &= ~alarm;
}
}
// wait to gather all necessary data to compute average time
double execTime = m_dbCounters[TIME_IDX] - m_prevDbCounters[TIME_IDX];
double queriesNo = (m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]) - (m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]);
if (queriesNo > 0 && (execTime / queriesNo / 1000) >= m_thresholds[TIME_IDX]) {
if ((m_alarms & EXEC_ALARM) == 0) {
m_alarms |= EXEC_ALARM;
m_alarmCounters[TIME_IDX]++;
__plugin.sendTrap(lookup(ExecTooLongAlrm,s_dbAccountQueries,""),toString(),index());
}
}
else
m_alarms &= ~EXEC_ALARM;
}
void DatabaseAccount::updateConfig(const NamedList* cfg)
{
if (!cfg)
return;
for (int i = 0; i <= MaxExecTime - MaxQueries; i++)
m_thresholds[i] = cfg->getIntValue(lookup(MaxQueries + i,s_dbAccountInfo,""),0);
m_resetInterval = cfg->getIntValue("notiftime",3600);
if (m_resetTime > Time::secNow() + m_resetInterval)
m_resetTime = Time::secNow() + m_resetInterval;
m_isCurrent = true;
}
// obtain information from this entry
const String DatabaseAccount::getInfo(unsigned int query)
{
DDebug(&__plugin,DebugAll,"DatabaseAccount::getInfo('%s') [%p]",lookup(query,s_dbAccountQueries,""),this);
String ret = "";
double execTime = 0;
double queriesNo = 0;
switch (query) {
case QueriesCount:
case FailedCount:
case ErrorsCount:
ret << m_dbCounters[query - 1] - m_prevDbCounters[query - 1];
break;
case ExecTime:
execTime = m_dbCounters[TIME_IDX] - m_prevDbCounters[TIME_IDX];
queriesNo = (m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]) - (m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]);
if (queriesNo > 0)
ret << (unsigned int) (execTime / queriesNo / 1000);
else
ret << 0;
break;
case TooManyAlrmCount:
case TooManyFailedAlrmCount:
case TooManyErrorAlrmCount:
case ExecTooLongAlrmCount:
case NoConnAlrmCount:
ret << m_alarmCounters[query - TooManyAlrmCount];
break;
case MaxQueries:
case MaxFailedQueries:
case MaxErrorQueries:
case MaxExecTime:
ret << m_thresholds[query - MaxQueries];
break;
case AccountName:
ret = m_name;
break;
case AccountIndex:
ret = index();
break;
default:
break;
}
return ret;
}
void DatabaseAccount::reset()
{
if (Time::secNow() < m_resetTime)
return;
__plugin.sendTrap(lookup(QueriesCount,s_dbAccountQueries,""),String(m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]),index());
__plugin.sendTrap(lookup(FailedCount,s_dbAccountQueries,""),String(m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]),index());
__plugin.sendTrap(lookup(ErrorsCount,s_dbAccountQueries,""),String(m_dbCounters[ERROR_IDX] - m_prevDbCounters[ERROR_IDX]),index());
double execTime = m_dbCounters[TIME_IDX] - m_prevDbCounters[TIME_IDX];
double queriesNo = (m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]) - (m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]);
unsigned int time = 0;
if (queriesNo > 0)
time = (unsigned int) (execTime / queriesNo / 1000);
__plugin.sendTrap(lookup(ExecTime,s_dbAccountQueries,""),String(time),index());
m_alarms = 0;
for (unsigned int i = 0; i < CONN_IDX; i++)
m_prevDbCounters[i] = m_dbCounters[i];
m_resetTime = Time::secNow() + m_resetInterval;
}
/**
* DatabaseInfo
*/
// parse database status information and store it for all sql modules
bool DatabaseInfo::load()
{
DDebug(&__plugin,DebugInfo,"DatabaseInfo::load() [%p] - loading data",this);
String modules[] = {"pgsqldb", "mysqldb"};
unsigned int i = 0;
for (int i = 0; i < FailedConns; i++)
m_connData[i] = 0;
while (i < 2) {
Message msg("engine.status");
msg.addParam("module",modules[i]);
msg.addParam("details","false");
Engine::dispatch(msg);
String& status = msg.retValue();
if (!TelEngine::null(status)) {
cutNewLine(status);
/* status example: name=mysqldb,type=database,format=Total|Failed|Errors|AvgExecTime;conns=1,failed=1 */
int pos = status.rfind(';');
if (pos < 0)
continue;
String connInfo = status.substr(pos + 1);
if (connInfo.null())
continue;
ObjList* l = connInfo.split(',');
for (ObjList* j = l->skipNull(); j; j = j->skipNext()) {
String* str = static_cast<String*>(j->get());
pos = str->find("=");
if (pos < 0)
continue;
String param = str->substr(0,pos);
String value = str->substr(pos + 1);
int type = lookup(param,s_databaseInfo,0);
if (!type)
continue;
m_connData[type - 1] += value.toInteger();
}
TelEngine::destruct(l);
}
i++;
}
updateExpire();
return true;
}
// get the answer to a query for the entry with the given index
String DatabaseInfo::getInfo(const String& query, unsigned int& index, TokenDict* dict)
{
DDebug(&__plugin,DebugAll,"DatabaseInfo::getInfo(query='%s',index='%d',dict='%p') [%p]",query.c_str(),index,dict,this);
// handle a query about an entry in the table
Lock l(this);
int type = lookup(query,s_dbAccountQueries,0);
if (type) {
if (index == 0 || index > m_table.count())
return "";
DatabaseAccount* acc = static_cast<DatabaseAccount*>(m_table[index - 1]);
if (!acc)
return "";
return acc->getInfo(type);
}
if (!isExpired())
// if the data has not yet expired, update the expire time
updateExpire();
// if the is no data available, obtain it from an engine.status message
if (m_reload && !load())
return "";
// handle queries about connections
type = lookup(query,s_databaseQuery,0);
switch (type) {
case Accounts:
return String(m_table.count());
case Connections:
case FailedConns:
return String(m_connData[type - 1]);
default:
break;
}
return "";
}
void DatabaseInfo::reset()
{
Lock l(this);
for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
DatabaseAccount* acc = static_cast<DatabaseAccount*>(o->get());
acc->reset();
}
}
// create a new DatabaseAccount object for monitoring a database
void DatabaseInfo::addDatabase(NamedList* cfg)
{
if (!cfg || !m_monitor)
return;
DDebug(&__plugin,DebugInfo,"DatabaseInfo::addDatabase('%s') [%p]",cfg->c_str(),this);
lock();
DatabaseAccount* acc = static_cast<DatabaseAccount*>(m_table[*cfg]);
if (!acc) {
acc = new DatabaseAccount(cfg);;
m_table.append(acc);
acc->setIndex(m_table.count());
}
else
acc->updateConfig(cfg);
unlock();
}
void DatabaseInfo::updateDatabaseAccounts()
{
lock();
bool deletedRoute = true;
while (deletedRoute) {
deletedRoute = false;
for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
DatabaseAccount* acc = static_cast<DatabaseAccount*>(o->get());
if (!acc->isCurrent()) {
DDebug(__plugin.name(),DebugAll,"DatabaseInfo::updateDatabaseAccounts() - removed database account '%s' from monitoring",
acc->toString().c_str());
m_table.remove(acc);
deletedRoute = true;
}
}
}
unsigned int index = 1;
for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
DatabaseAccount* acc = static_cast<DatabaseAccount*>(o->get());
acc->setIsCurrent(false);
acc->setIndex(index);
index++;
}
unlock();
}
//update the information for a given account
void DatabaseInfo::update(const Message& msg)
{
XDebug(&__plugin,DebugInfo,"DatabaseInfo::update()");
int count = msg.getIntValue("count",0);
for (int i = 0; i < count; i++) {
String str = s_dbParam;
str << i;
String acc = msg.getValue(str);
DatabaseAccount* dbAccount = static_cast<DatabaseAccount*>(m_table[acc]);
if (!dbAccount)
continue;
NamedList nl(acc);
str = s_totalParam;
str << i;
nl.setParam("total",msg.getValue(str));
str = s_failedParam;
str << i;
nl.setParam("failed",msg.getValue(str));
str = s_errorParam;
str << i;
nl.setParam("errorred",msg.getValue(str));
str = s_hasConnParam;
str << i;
nl.setParam("hasconn",msg.getValue(str));
str = s_timeParam;
str << i;
nl.setParam("querytime",msg.getValue(str));
dbAccount->update(nl);
}
}
/**
* RTPEntry
*/
RTPEntry::RTPEntry(String rtpDirection)
: m_rtpDir(rtpDirection), m_index(0),
m_isCurrent(true)
{
Debug(&__plugin,DebugAll,"RTPEntry '%s' created [%p]",rtpDirection.c_str(),this);
reset();
}
RTPEntry::~RTPEntry()
{
Debug(&__plugin,DebugAll,"RTPEntry '%s' destroyed [%p]",m_rtpDir.c_str(),this);
}
// update the RTP info from the given list
void RTPEntry::update(const NamedList& nl)
{
DDebug(&__plugin,DebugAll,"RTPEntry::update() name='%s' [%p]",m_rtpDir.c_str(),this);
for (unsigned int i = 0; i < nl.count(); i++) {
NamedString* n = nl.getParam(i);
if (!n)
continue;
int type = lookup(n->name(),s_rtpInfo,0);
if (!type || type < NoAudio)
continue;
m_counters[type - NoAudio] += (*n).toInteger();
}
}
// reset counters
void RTPEntry::reset()
{
DDebug(&__plugin,DebugAll,"RTPEntry::reset() '%s' [%p]",m_rtpDir.c_str(),this);
for (int i = 0; i < WrongSSRC - Direction; i++)
m_counters[i] = 0;
}
// the the answer to a query about this RTP direction
String RTPEntry::getInfo(unsigned int query)
{
DDebug(&__plugin,DebugAll,"RTPEntry::getInfo('%s') '%s' [%p]",lookup(query,s_rtpQuery,""),m_rtpDir.c_str(),this);
String retStr = "";
switch (query) {
case Direction:
retStr << m_rtpDir;
break;
case Index:
retStr << m_index;
break;
case NoAudio:
case LostAudio:
case PktsLost:
case SyncLost:
case SeqLost:
case WrongSRC:
case WrongSSRC:
retStr << m_counters[query - NoAudio];
break;
default:
break;
}
return retStr;
}
/**
* RTPTable
*/
RTPTable::RTPTable(const NamedList* cfg)
: m_rtpMtx(false,"Monitor::rtpInfo"),
m_resetInterval(3600), m_monitor(false)
{
Debug(&__plugin,DebugAll,"RTPTable created [%p]",this);
// build RTPEntry objects for monitoring RTP directions if monitoring is enabled
reconfigure(cfg);
}
void RTPTable::reconfigure(const NamedList* cfg)
{
if (!cfg)
return;
m_monitor = cfg->getBoolValue("monitor",false);
m_resetInterval = cfg->getIntValue("reset_interval",3600);
m_rtpMtx.lock();
if (!m_monitor)
m_rtpEntries.clear();
String directions = cfg->getValue("rtp_directions","");
Debug(&__plugin,DebugAll,"RTPTable [%p] configured with directions='%s',resetTime=" FMT64U,this,directions.c_str(),m_resetInterval);
if (m_monitor) {
ObjList* l = directions.split(',');
for (ObjList* o = l->skipNull(); o; o = o->skipNext()) {
String* str = static_cast<String*>(o->get());
RTPEntry* entry = static_cast<RTPEntry*>(m_rtpEntries[*str]);
if (!entry) {
entry = new RTPEntry(*str);
m_rtpEntries.append(entry);
entry->setIndex(m_rtpEntries.count());
}
else
entry->setIsCurrent(true);
}
TelEngine::destruct(l);
}
bool deletedDir = true;
while (deletedDir) {
deletedDir = false;
for (ObjList* o = m_rtpEntries.skipNull(); o; o = o->skipNext()) {
RTPEntry* entry = static_cast<RTPEntry*>(o->get());
if (!entry->isCurrent()) {
DDebug(__plugin.name(),DebugAll,"RTPTable::reconfigure() - removed direction '%s' from monitoring",entry->toString().c_str());
m_rtpEntries.remove(entry);
deletedDir = true;
}
}
}
unsigned int index = 1;
for (ObjList* o = m_rtpEntries.skipNull(); o; o = o->skipNext()) {
RTPEntry* entry = static_cast<RTPEntry*>(o->get());
entry->setIsCurrent(false);
entry->setIndex(index);
index++;
}
m_rtpMtx.unlock();
m_resetTime = Time::secNow() + m_resetInterval;
}
// update an entry
void RTPTable::update(Message& msg)
{
XDebug(&__plugin,DebugAll,"RTPTable::update() [%p]",this);
String dir = lookup(RTPEntry::Direction,RTPEntry::s_rtpInfo,"");
if (dir.null())
dir = "remoteip";
String rtpDir = msg.getValue(dir,"");
if (rtpDir.null())
return;
m_rtpMtx.lock();
RTPEntry* entry = static_cast<RTPEntry*>(m_rtpEntries[rtpDir]);
if (entry)
entry->update(msg);
m_rtpMtx.unlock();
}
String RTPTable::getInfo(const String& query, const unsigned int& index)
{
DDebug(&__plugin,DebugAll,"RTPTable::getInfo(query='%s',index='%u') [%p]",query.c_str(),index,this);
String retStr = "";
int type = lookup(query,s_rtpQuery,0);
if (!type)
return retStr;
if (type == RTPEntry::Count)
retStr << m_rtpEntries.count();
else if (index > 0 && index <= m_rtpEntries.count()) {
m_rtpMtx.lock();
RTPEntry* entry = static_cast<RTPEntry*>(m_rtpEntries[index - 1]);
if (entry)
retStr << entry->getInfo(type);
m_rtpMtx.unlock();
}
return retStr;
}
// reset information
void RTPTable::reset()
{
XDebug(&__plugin,DebugAll,"RTPTable::reset() [%p]",this);
m_rtpMtx.lock();
for (ObjList* o = m_rtpEntries.skipNull(); o; o = o->skipNext()) {
RTPEntry* e = static_cast<RTPEntry*>(o->get());
e->reset();
}
m_rtpMtx.unlock();
m_resetTime = Time::secNow() + m_resetInterval;
}
/**
* CallRouteQoS
*/
CallRouteQoS::CallRouteQoS(const String direction, const NamedList* cfg)
: m_routeName(direction)
{
Debug(&__plugin,DebugAll,"CallRouteQoS [%p] created for route '%s',cfg='%p'",this,direction.c_str(),cfg);
for (int i = 0; i < NO_CAUSE - HANGUP; i++) {
m_callCounters[i] = 0;
m_callCountersAll[i] = 0;
}
for (int i = 0; i <= TOTAL_IDX; i++) {
m_totalCalls[i] = 0;
m_answeredCalls[i] = 0;
m_delivCalls[i] = 0;
}
for (int i = 0; i <= NER_LOW_ALL; i++)
m_alarmCounters[i] = 0;
m_alarms = 0;
m_overallAlarms = 0;
m_alarmsSent = 0;
m_overallAlarmsSent = 0;
m_minCalls = 1;
m_minASR = m_maxASR = m_minNER = -1;
if (cfg)
updateConfig(cfg);
m_index = 0;
}
CallRouteQoS::~CallRouteQoS()
{
Debug(&__plugin,DebugAll,"CallRouteQoS [%p] destroyed",this);
}
void CallRouteQoS::updateConfig(const NamedList* cfg)
{
if (!cfg)
return;
m_minCalls = cfg->getIntValue("mincalls",m_minCalls);
m_minASR = cfg->getIntValue("minASR",m_minASR);
if (m_minASR > 100 || m_minASR < -1) {
Debug(&__plugin,DebugNote,"CallRouteQoS::updateConfig() - route '%s': configured minASR is not in the -1..100 interval, "
"defaulting to -1",m_routeName.c_str());
m_minASR = -1;
}
m_maxASR = cfg->getIntValue("maxASR",m_maxASR);
if (m_maxASR > 100 || m_maxASR < -1) {
Debug(&__plugin,DebugNote,"CallRouteQoS::updateConfig() - route '%s': configured maxASR is not in the -1..100 interval, "
"defaulting to -1",m_routeName.c_str());
m_maxASR = -1;
}
m_minNER = cfg->getIntValue("minNER",m_minNER);
if (m_minNER > 100 || m_minNER < -1) {
Debug(&__plugin,DebugNote,"CallRouteQoS::updateConfig() - route '%s': configured minNER is not in the -1..100 interval, "
"defaulting to -1",m_routeName.c_str());
m_minNER = -1;
}
m_isCurrent = true;
}
// update the counters taking into account the type of the call and reason with which the call was ended
void CallRouteQoS::update(int type, int endReason)
{
DDebug(&__plugin,DebugAll,"CallRouteQoS::update(callType='%d',endReason='%d') [%p] ",type,endReason,this);
m_totalCalls[CURRENT_IDX]++;
m_totalCalls[TOTAL_IDX]++;
switch (type) {
case ANSWERED:
m_answeredCalls[CURRENT_IDX]++;
m_answeredCalls[TOTAL_IDX]++;
break;
case DELIVERED:
m_delivCalls[CURRENT_IDX]++;
m_delivCalls[TOTAL_IDX]++;
break;
default:
break;
}
if (endReason != -1 && endReason >= HANGUP && endReason < NO_CAUSE) {
m_callCounters[endReason - HANGUP]++;
m_callCountersAll[endReason - HANGUP]++;
}
}
// update the internal ASR/NER values and check for alarms
void CallRouteQoS::updateQoS()
{
//XDebug(&__plugin,DebugAll,"CallRouteQoS::updateQoS() [%p]",this);
int realASR, totalASR;
if ((m_totalCalls[CURRENT_IDX] != m_totalCalls[PREVIOUS_IDX]) && (m_totalCalls[CURRENT_IDX] >= m_minCalls)) {
float currentHyst = 50.0 / m_totalCalls[CURRENT_IDX] * s_qosHysteresisFactor;
float totalHyst = 50.0 / m_totalCalls[TOTAL_IDX] * s_qosHysteresisFactor;
realASR = (int) (m_answeredCalls[CURRENT_IDX] * 100.0 / m_totalCalls[CURRENT_IDX]);
checkForAlarm(realASR,currentHyst,m_alarms,m_minASR,m_maxASR,LOW_ASR,HIGH_ASR);
m_totalCalls[PREVIOUS_IDX] = m_totalCalls[CURRENT_IDX];
totalASR = (int) (m_answeredCalls[TOTAL_IDX] * 100.0 / m_totalCalls[TOTAL_IDX]);
checkForAlarm(totalASR,totalHyst,m_overallAlarms,m_minASR,m_maxASR,LOW_ASR,HIGH_ASR);
int ner = (int) ((m_answeredCalls[CURRENT_IDX] + m_delivCalls[CURRENT_IDX]) * 100.0 / m_totalCalls[CURRENT_IDX]);
checkForAlarm(ner,currentHyst,m_alarms,m_minNER,-1,LOW_NER);
ner = (int) ((m_answeredCalls[TOTAL_IDX] + m_delivCalls[TOTAL_IDX]) * 100.0 / m_totalCalls[TOTAL_IDX]);
checkForAlarm(ner,totalHyst,m_overallAlarms,m_minNER,-1,LOW_NER);
}
}
// reset counters
void CallRouteQoS::reset()
{
DDebug(&__plugin,DebugInfo,"CallRoute::reset() [%p]",this);
m_totalCalls[CURRENT_IDX] = m_totalCalls[PREVIOUS_IDX] = 0;
m_answeredCalls[CURRENT_IDX] = m_answeredCalls[PREVIOUS_IDX] = 0;
m_delivCalls[CURRENT_IDX] = m_delivCalls[PREVIOUS_IDX] = 0;
m_alarms = 0;
m_alarmsSent = 0;
m_alarmCounters[ASR_LOW] = m_alarmCounters[ASR_HIGH] = m_alarmCounters[NER_LOW] = 0;
for (int i = 0; i < NO_CAUSE - HANGUP; i++)
m_callCounters[i] = 0;
}
// check a value against a threshold and if necessary set an alarm
void CallRouteQoS::checkForAlarm(int& value, float hysteresis, u_int8_t& alarm, const int min,
const int max, u_int8_t minAlarm, u_int8_t maxAlarm)
{
if (min >= 0) {
float hystValue = (alarm & minAlarm ? value - hysteresis : value + hysteresis);
if (hystValue <= min)
alarm |= minAlarm;
else
alarm &= ~minAlarm;
}
if (max >= 0) {
float hystValue = (alarm & maxAlarm ? value + hysteresis : value - hysteresis);
if (hystValue >= max)
alarm |= maxAlarm;
else
alarm &= ~maxAlarm;
}
}
// is this entry in an alarm state?
bool CallRouteQoS::alarm()
{
if (m_alarms || m_overallAlarms)
return true;
m_alarmsSent = m_overallAlarmsSent = 0;
return false;
}
// get the string version of the alarm and remember that we sent this alarm (avoid sending the same alarm multiple times)
const String CallRouteQoS::alarmText()
{
String text = "";
if (m_alarms & LOW_ASR) {
if (!(m_alarmsSent & LOW_ASR)) {
m_alarmsSent |= LOW_ASR;
m_alarmCounters[ASR_LOW]++;
return text = lookup(ASR_LOW,s_callQualityQueries,"");
}
}
else
m_alarmsSent &= ~LOW_ASR;
if (m_alarms & HIGH_ASR) {
if (!(m_alarmsSent & HIGH_ASR)) {
m_alarmsSent |= HIGH_ASR;
m_alarmCounters[ASR_HIGH]++;
return text = lookup(ASR_HIGH,s_callQualityQueries,"");
}
}
else
m_alarmsSent &= ~HIGH_ASR;
if (m_alarms & LOW_NER) {
if (!(m_alarmsSent & LOW_NER)) {
m_alarmsSent |= LOW_NER;
m_alarmCounters[NER_LOW]++;
return text = lookup(NER_LOW,s_callQualityQueries,"");
}
}
else
m_alarmsSent &= ~LOW_NER;
if (m_overallAlarms & LOW_ASR) {
if (!(m_overallAlarmsSent & LOW_ASR)) {
m_overallAlarmsSent |= LOW_ASR;
m_alarmCounters[ASR_LOW_ALL]++;
return text = lookup(ASR_LOW_ALL,s_callQualityQueries,"");
}
}
else
m_overallAlarmsSent &= ~LOW_ASR;
if (m_overallAlarms & HIGH_ASR) {
if (!(m_overallAlarmsSent & HIGH_ASR)) {
m_overallAlarmsSent |= HIGH_ASR;
m_alarmCounters[ASR_HIGH_ALL]++;
return text = lookup(ASR_HIGH_ALL,s_callQualityQueries,"");
}
}
else
m_overallAlarmsSent &= ~HIGH_ASR;
if (m_overallAlarms & LOW_NER) {
if (!(m_overallAlarmsSent & LOW_NER)) {
m_overallAlarmsSent |= LOW_NER;
m_alarmCounters[NER_LOW_ALL]++;
return text = lookup(NER_LOW_ALL,s_callQualityQueries,"");
}
}
else
m_overallAlarmsSent &= ~LOW_NER;
return text;
}
// send periodical notification containing current ASR and NER values
void CallRouteQoS::sendNotifs(unsigned int index, bool rst)
{
DDebug(&__plugin,DebugInfo,"CallRouteQoS::sendNotifs() - route='%s' reset=%s [%p]",toString().c_str(),String::boolText(rst),this);
// we dont want notifcations if we didn't have the minimum number of calls
if (m_totalCalls[CURRENT_IDX] >= m_minCalls) {
NamedList nl("");
String value;
nl.addParam("index",String(index));
nl.addParam("count","4");
for (int i = 0; i < 4; i++) {
String param = "notify.";
param << i;
String paramValue = "value.";
paramValue << i;
nl.addParam(param,lookup(ASR + i,s_callQualityQueries,""));
get(ASR + i,value);
nl.addParam(paramValue,value);
}
__plugin.sendTraps(nl);
}
if (rst)
reset();
}
// get the value for a query
bool CallRouteQoS::get(int query, String& result)
{
DDebug(&__plugin,DebugInfo,"CallRouteQoS::get(query='%s') [%p]",lookup(query,s_callQualityQueries,""),this);
int val = 0;
if (query) {
switch (query) {
case ASR:
if (m_totalCalls[CURRENT_IDX]) {
val = (int) (m_answeredCalls[CURRENT_IDX] * 100.0 / m_totalCalls[CURRENT_IDX]);
result = String( val);
}
else
result = "-1";
return true;
case NER:
if (m_totalCalls[CURRENT_IDX]) {
val = (int) ((m_answeredCalls[CURRENT_IDX] + m_delivCalls[CURRENT_IDX]) * 100.0 / m_totalCalls[CURRENT_IDX]);
result = String(val);
}
else
result = "-1";
return true;
case ASR_ALL:
if (m_totalCalls[TOTAL_IDX]) {
val = (int) (m_answeredCalls[TOTAL_IDX] * 100.0 / m_totalCalls[TOTAL_IDX]);
result = String(val);
}
else
result = "-1";
return true;
case NER_ALL:
if (m_totalCalls[TOTAL_IDX]) {
val = (int) ((m_answeredCalls[TOTAL_IDX] + m_delivCalls[TOTAL_IDX]) * 100.0 / m_totalCalls[TOTAL_IDX]);
result = String(val);
}
else
result = "-1";
return true;
case MIN_ASR:
result << m_minASR;
return true;
case MAX_ASR:
result << m_maxASR;
return true;
case MIN_NER:
result << m_minNER;
return true;
case LOW_ASR_COUNT:
case HIGH_ASR_COUNT:
case LOW_ASR_ALL_COUNT:
case HIGH_ASR_ALL_COUNT:
case LOW_NER_COUNT:
case LOW_NER_ALL_COUNT:
result << m_alarmCounters[query - LOW_ASR_COUNT + 1];
return true;
case HANGUP:
case REJECT:
case BUSY:
case CANCELLED:
case NO_ANSWER:
case NO_ROUTE:
case NO_CONN:
case NO_AUTH:
case CONGESTION:
case NO_MEDIA:
result << m_callCounters[query - HANGUP];
return true;
case HANGUP_ALL:
case REJECT_ALL:
case BUSY_ALL:
case CANCELLED_ALL:
case NO_ANSWER_ALL:
case NO_ROUTE_ALL:
case NO_CONN_ALL:
case NO_AUTH_ALL:
case CONGESTION_ALL:
case NO_MEDIA_ALL:
result << m_callCountersAll[query - HANGUP_ALL];
return true;
case NAME:
result << toString();
return true;
case INDEX:
result << m_index;
return true;
default:
return false;
}
}
return false;
}
/**
* CallMonitor
*/
CallMonitor::CallMonitor(const NamedList* sect, unsigned int priority)
: MessageHandler("call.cdr",priority),
Thread("Call Monitor"),
m_checkTime(3600),
m_notifTime(0), m_inCalls(0), m_outCalls(0), m_first(true),
m_routeParam("address"),
m_monitor(false)
{
setFilter("operation","finalize");
// configure
setConfigure(sect);
m_notifTime = Time::secNow() + m_checkTime;
init();
}
bool CallMonitor::init()
{
return startup();
}
// main loop. Update the monitor data, if neccessary, send alarms and/or notifications
void CallMonitor::run()
{
for (;;) {
check();
idle();
bool sendNotif = false;
m_cfgMtx.lock();
if (!m_first && Time::secNow() >= m_notifTime) {
m_notifTime = Time::secNow() + m_checkTime;
sendNotif = true;
}
m_cfgMtx.unlock();
m_routesMtx.lock();
unsigned int index = 0;
for (ObjList* o = m_routes.skipNull(); o; o = o->skipNext()) {
index++;
CallRouteQoS* route = static_cast<CallRouteQoS*>(o->get());
route->updateQoS();
if (route->alarm())
sendAlarmFrom(route);
if (sendNotif)
route->sendNotifs(index,true);
}
if (sendNotif)
sendNotif = false;
if (m_first)
m_first = false;
m_routesMtx.unlock();
}
}
// set configuration
void CallMonitor::setConfigure(const NamedList* sect)
{
if (!sect)
return;
m_cfgMtx.lock();
m_checkTime = sect ? sect->getIntValue("time_interval",3600) : 3600;
m_routeParam = sect ? sect->getValue("route","address") : "address";
m_monitor = sect ? sect->getBoolValue("monitor",false) : false;
if (!m_monitor)
m_routes.clear();
// if the previous time for notification is later than the one with the new interval, reset it
if (m_notifTime > Time::secNow() + m_checkTime)
m_notifTime = Time::secNow() + m_checkTime;
s_qosHysteresisFactor = sect ? sect->getDoubleValue("hysteresis_factor",2.0) : 2.0;
if (s_qosHysteresisFactor > 10 || s_qosHysteresisFactor < 1.0) {
Debug(&__plugin,DebugNote,"CallMonitor::setConfigure() - configured hysteresis_factor is not in the 1.0 - 10.0 interval,"
" defaulting to 2.0");
s_qosHysteresisFactor = 2.0;
}
m_cfgMtx.unlock();
}
// add a route to be monitored
void CallMonitor::addRoute(NamedList* cfg)
{
if (!m_monitor || !cfg)
return;
m_routesMtx.lock();
CallRouteQoS* route = static_cast<CallRouteQoS*>(m_routes[*cfg]);
if (!route) {
route = new CallRouteQoS(*cfg,cfg);
m_routes.append(route);
route->setIndex(m_routes.count());
}
else
route->updateConfig(cfg);
m_routesMtx.unlock();
}
void CallMonitor::updateRoutes()
{
m_routesMtx.lock();
bool deletedRoute = true;
while (deletedRoute) {
deletedRoute = false;
for (ObjList* o = m_routes.skipNull(); o; o = o->skipNext()) {
CallRouteQoS* route = static_cast<CallRouteQoS*>(o->get());
if (!route->isCurrent()) {
DDebug(__plugin.name(),DebugAll,"CallMonitor::updateRoutes() - removed route '%s' from monitoring",route->toString().c_str());
m_routes.remove(route);
deletedRoute = true;
}
}
}
unsigned int index = 1;
for (ObjList* o = m_routes.skipNull(); o; o = o->skipNext()) {
CallRouteQoS* route = static_cast<CallRouteQoS*>(o->get());
route->setIsCurrent(false);
route->setIndex(index);
index++;
}
m_routesMtx.unlock();
}
// send an alarm received from a route
void CallMonitor::sendAlarmFrom(CallRouteQoS* route)
{
if (!route)
return;
String alarm = route->alarmText();
if (!alarm.null())
__plugin.sendTrap(alarm,route->toString());
}
// extract from a call.cdr message call information and update the monitor data
bool CallMonitor::received(Message& msg)
{
DDebug(__plugin.name(),DebugAll,"CdrHandler::received()");
if (m_routeParam.null())
return false;
String routeStr = msg.getValue(m_routeParam);
if (routeStr.null())
return false;
if (m_routeParam == YSTRING("address")) {
int pos = routeStr.rfind(':');
if (pos < 0)
pos = routeStr.rfind('/');
if (pos > 0)
routeStr = routeStr.substr(0,pos);
}
const String& status = msg[YSTRING("status")];
int code = -1;
if (status == YSTRING("answered"))
code = CallRouteQoS::ANSWERED;
else if (status == YSTRING("ringing") || status == YSTRING("accepted"))
code = CallRouteQoS::DELIVERED;
const String& direction = msg[YSTRING("direction")];
bool outgoing = false;
if (msg.getBoolValue("cdrwrite",true)) {
if (direction == YSTRING("incoming"))
m_inCalls++;
else if (direction == YSTRING("outgoing")) {
outgoing = true;
m_outCalls++;
}
}
const String& reason = msg[YSTRING("reason")];
int type = lookup(reason,s_endReasons,CallRouteQoS::HANGUP);
if (type == CallRouteQoS::HANGUP && code == CallRouteQoS::DELIVERED && outgoing)
type = CallRouteQoS::CANCELLED;
else if (type <= CallRouteQoS::NO_ANSWER && !outgoing)
type = CallRouteQoS::HANGUP;
m_routesMtx.lock();
CallRouteQoS* route = static_cast<CallRouteQoS*>(m_routes[routeStr]);
if (route)
route->update(code,type);
m_routesMtx.unlock();
return false;
}
// get a value from the call counters
bool CallMonitor::getCounter(int type, unsigned int& value)
{
DDebug(__plugin.name(),DebugAll,"CallMonitor::getCounter(%s)",lookup(type,s_callCounterQueries,""));
if (type == 0 || type > ROUTES_COUNT)
return false;
switch (type) {
case INCOMING_CALLS:
value = m_inCalls;
break;
case OUTGOING_CALLS:
value = m_outCalls;
break;
case ROUTES_COUNT:
value = m_routes.count();
break;
default:
return false;
}
return true;
}
// obtain call monitor specific data
void CallMonitor::get(const String& query, const int& index, String& result)
{
DDebug(__plugin.name(),DebugAll,"CallMonitor::get(%s,%d)",query.c_str(),index);
if (index > 0) {
CallRouteQoS* route = static_cast<CallRouteQoS*>(m_routes[index - 1]);
if (!route)
return;
int type = lookup(query,s_callQualityQueries,0);
if (type && route->get(type,result))
return;
}
int type = lookup(query,s_callCounterQueries,0);
unsigned int value = 0;
if (getCounter(type,value)) {
result += value;
return;
}
}
/**
* Monitor
*/
Monitor::Monitor()
: Module("monitoring","misc"),
m_msgUpdateHandler(0),
m_snmpMsgHandler(0),
m_hangupHandler(0),
m_startHandler(0),
m_callMonitor(0),
m_authHandler(0),
m_registerHandler(0),
m_init(false),
m_newTraps(false),
m_sipMonitoredGws(0),
m_trunkMon(false),
m_linksetMon(false),
m_linkMon(false),
m_interfaceMon(false),
m_isdnMon(false),
m_activeCallsCache(0),
m_trunkInfo(0),
m_engineInfo(0),
m_moduleInfo(0),
m_dbInfo(0),
m_rtpInfo(0),
m_linksetInfo(0),
m_linkInfo(0),
m_ifaceInfo(0),
m_accountsInfo(0)
{
Output("Loaded module Monitoring");
}
Monitor::~Monitor()
{
Output("Unloaded module Monitoring");
Debugger::setAlarmHook();
TelEngine::destruct(m_moduleInfo);
TelEngine::destruct(m_engineInfo);
TelEngine::destruct(m_activeCallsCache);
TelEngine::destruct(m_linkInfo);
TelEngine::destruct(m_linksetInfo);
TelEngine::destruct(m_trunkInfo);
TelEngine::destruct(m_dbInfo);
TelEngine::destruct(m_rtpInfo);
TelEngine::destruct(m_ifaceInfo);
TelEngine::destruct(m_accountsInfo);
TelEngine::destruct(m_sipMonitoredGws);
TelEngine::destruct(m_msgUpdateHandler);
TelEngine::destruct(m_snmpMsgHandler);
TelEngine::destruct(m_startHandler);
TelEngine::destruct(m_authHandler);
TelEngine::destruct(m_registerHandler);
TelEngine::destruct(m_hangupHandler);
}
bool Monitor::unload()
{
DDebug(this,DebugAll,"::unload()");
if (!lock(500000))
return false;
Engine::uninstall(m_msgUpdateHandler);
Engine::uninstall(m_snmpMsgHandler);
Engine::uninstall(m_startHandler);
Engine::uninstall(m_authHandler);
Engine::uninstall(m_registerHandler);
Engine::uninstall(m_hangupHandler);
if (m_callMonitor) {
Engine::uninstall(m_callMonitor);
m_callMonitor->cancel();
m_callMonitor = 0;
}
uninstallRelays();
unlock();
return true;
}
void Monitor::initialize()
{
Output("Initializing module Monitoring");
// read configuration
Configuration cfg(Engine::configFile("monitoring"));
if (!m_init) {
m_init = true;
setup();
installRelay(Halt);
installRelay(Timer);
Debugger::setAlarmHook(alarmCallback);
s_nodeState = "active";
}
if (!m_msgUpdateHandler) {
m_msgUpdateHandler = new MsgUpdateHandler();
Engine::install(m_msgUpdateHandler);
}
if (!m_snmpMsgHandler) {
m_snmpMsgHandler = new SnmpMsgHandler();
Engine::install(m_snmpMsgHandler);
}
if (!m_hangupHandler) {
m_hangupHandler = new HangupHandler();
Engine::install(m_hangupHandler);
}
if (!m_startHandler) {
m_startHandler = new EngineStartHandler();
Engine::install(m_startHandler);
}
if (!m_authHandler) {
m_authHandler = new AuthHandler();
Engine::install(m_authHandler);
}
if (!m_registerHandler) {
m_registerHandler = new RegisterHandler();
Engine::install(m_registerHandler);
}
// build a call monitor
NamedList* asrCfg = cfg.getSection("call_qos");
if (!m_callMonitor) {
m_callMonitor = new CallMonitor(asrCfg);
Engine::install(m_callMonitor);
}
else
m_callMonitor->setConfigure(asrCfg);
int cacheFor = cfg.getIntValue("general","cache",1);
if (!m_activeCallsCache)
m_activeCallsCache = new ActiveCallsInfo();
m_activeCallsCache->setRetainInfoTime(cacheFor);//seconds
if (!m_trunkInfo)
m_trunkInfo = new TrunkInfo();
m_trunkInfo->setRetainInfoTime(cacheFor);//seconds
if (!m_linksetInfo)
m_linksetInfo = new LinksetInfo();
m_linksetInfo->setRetainInfoTime(cacheFor);//seconds
if (!m_linkInfo)
m_linkInfo = new LinkInfo();
m_linkInfo->setRetainInfoTime(cacheFor);//seconds
if (!m_ifaceInfo)
m_ifaceInfo = new InterfaceInfo();
m_ifaceInfo->setRetainInfoTime(cacheFor);//seconds
if (!m_accountsInfo)
m_accountsInfo = new AccountsInfo();
m_accountsInfo->setRetainInfoTime(cacheFor);//seconds
if (!m_engineInfo)
m_engineInfo = new EngineInfo();
m_engineInfo->setRetainInfoTime(cacheFor);//seconds
if (!m_moduleInfo)
m_moduleInfo = new ModuleInfo();
m_moduleInfo->setRetainInfoTime(cacheFor);//seconds
bool enable = cfg.getBoolValue("database","monitor",false);
if (!m_dbInfo)
m_dbInfo = new DatabaseInfo(enable);
else
m_dbInfo->setMonitorEnabled(enable);
m_dbInfo->setRetainInfoTime(cacheFor);
readConfig(cfg);
}
void Monitor::readConfig(const Configuration& cfg)
{
// get the threshold for yate restart alarm
s_yateRunAlarm = cfg.getIntValue("general","restart_alarm",1);
int level = cfg.getIntValue("general","alarm_threshold",DebugNote);
if (level < DebugFail)
level = -1;
else if (level < DebugConf)
level = DebugConf;
else if (level > DebugAll)
level = DebugAll;
s_alarmThreshold = level;
m_newTraps = !cfg.getBoolValue("general","old_trap_style");
// read configs for database monitoring (they type=database, the name of the section is the database account)
for (unsigned int i = 0; i < cfg.sections(); i++) {
NamedList* sec = cfg.getSection(i);
if (!sec || (*sec == "general"))
continue;
String type = sec->getValue("type","");
if (type.null())
continue;
if (type == "database" && m_dbInfo)
m_dbInfo->addDatabase(sec);
if (type == "call_qos" && m_callMonitor)
m_callMonitor->addRoute(sec);
}
m_callMonitor->updateRoutes();
m_dbInfo->updateDatabaseAccounts();
// read config for SIP monitoring
String gw = cfg.getValue("sip","gateways","");
if (!gw.null()) {
if (m_sipMonitoredGws)
TelEngine::destruct(m_sipMonitoredGws);
m_sipMonitoredGws = gw.split(';',false);
for (ObjList* o = m_sipMonitoredGws->skipNull(); o; o = o->skipNext()) {
String* addr = static_cast<String*>(o->get());
int pos = addr->find(":");
if (pos == -1)
addr->append(":" + String(SIP_PORT));
else {
String tmp = addr->substr(pos+1);
if (tmp.null())
addr->append(":" + String(SIP_PORT));
}
}
}
s_sipInfo.auths.threshold = cfg.getIntValue("sip","max_failed_auths",0);
s_sipInfo.transactions.threshold = cfg.getIntValue("sip","max_transaction_timeouts",0);
s_sipInfo.byes.threshold = cfg.getIntValue("sip","max_byes_timeouts",0);
s_sipInfo.reset = cfg.getIntValue("sip","reset_time",0);
if (s_sipInfo.reset)
s_sipInfo.resetTime = Time::secNow() + s_sipInfo.reset;
// read SS7 monitoring
bool sigEnable = cfg.getBoolValue("sig","monitor",false);
m_trunkMon = cfg.getBoolValue("sig","trunk",sigEnable);
m_interfaceMon = cfg.getBoolValue("sig","interface",sigEnable);
m_linksetMon = cfg.getBoolValue("sig","linkset",sigEnable);
m_linkMon = cfg.getBoolValue("sig","link",sigEnable);
m_isdnMon = cfg.getBoolValue("sig","isdn",sigEnable);
// read RTP monitoring
NamedList* sect = cfg.getSection("rtp");
if (sect) {
if (!m_rtpInfo)
m_rtpInfo = new RTPTable(sect);
else
m_rtpInfo->reconfigure(sect);
}
else
TelEngine::destruct(m_rtpInfo);
// read config for MGCP monitoring
s_mgcpInfo.transactions.threshold = cfg.getIntValue("mgcp","max_transaction_timeouts",0);
s_mgcpInfo.deletes.threshold = cfg.getIntValue("mgcp","max_deletes_timeouts",0);
s_mgcpInfo.reset = cfg.getIntValue("mgcp","reset_time",0);
s_mgcpInfo.gw_monitor = cfg.getBoolValue("mgcp","gw_monitor",false);
if (s_mgcpInfo.reset)
s_mgcpInfo.resetTime = Time::secNow() + s_mgcpInfo.reset;
}
// handle messages received by the module
bool Monitor::received(Message& msg, int id)
{
if (id == Halt) {
DDebug(this,DebugInfo,"::received() - Halt Message");
s_nodeState = "exiting";
unload();
}
if (id == Timer) {
if (m_rtpInfo && m_rtpInfo->shouldReset())
m_rtpInfo->reset();
if (s_sipInfo.resetTime && Time::secNow() > s_sipInfo.resetTime) {
s_sipInfo.auths.counter = s_sipInfo.transactions.counter = s_sipInfo.byes.counter = 0;
s_sipInfo.auths.alarm = s_sipInfo.transactions.alarm = s_sipInfo.byes.alarm = false;
s_sipInfo.resetTime = Time::secNow() + s_sipInfo.reset;
}
if (s_mgcpInfo.resetTime && Time::secNow() > s_mgcpInfo.resetTime) {
s_mgcpInfo.transactions.counter = s_mgcpInfo.deletes.counter = 0;
s_mgcpInfo.transactions.alarm = s_mgcpInfo.deletes.alarm = false;
s_mgcpInfo.resetTime = Time::secNow() + s_mgcpInfo.reset;
}
if (m_dbInfo)
m_dbInfo->reset();
}
return Module::received(msg,id);
}
// handle module.update messages
void Monitor::update(Message& msg)
{
String module = msg.getValue("module","");
XDebug(this,DebugAll,"Monitor::update() from module=%s",module.c_str());
int type = lookup(module,s_modules,0);
switch (type) {
case DATABASE:
if (m_dbInfo)
m_dbInfo->update(msg);
break;
case PSTN:
sendSigNotifs(msg);
break;
case INTERFACE:
sendCardNotifs(msg);
break;
case RTP:
if (m_rtpInfo)
m_rtpInfo->update(msg);
break;
case SIP:
case MGCP:
checkNotifs(msg,type);
break;
default:
break;
}
}
// build SS7 notifications
void Monitor::sendSigNotifs(Message& msg)
{
const String& type = msg[YSTRING("type")];
const String& name = msg[YSTRING("from")];
if (type.null() || name.null())
return;
// get the type of the notification
int t = lookup(type,s_sigTypes,0);
DDebug(this,DebugInfo,"Monitor::sendSigNotifs() - send notification from '%s'",name.c_str());
bool up = msg.getBoolValue(YSTRING("operational"));
const char* text = msg.getValue(YSTRING("text"));
String notif;
// build trap information
switch (t) {
case ISDN:
if (m_isdnMon)
sendTrap(lookup((up ? IsdnQ921Up : IsdnQ921Down),s_sigNotifs),name,0,text);
if (!up && m_linkInfo)
m_linkInfo->updateAlarmCounter(name);
break;
case SS7_MTP3:
if (m_linksetMon) {
sendTrap(lookup((up ? LinksetUp : LinksetDown),s_sigNotifs),name,0,text);
if (!up && m_linksetInfo)
m_linksetInfo->updateAlarmCounter(name);
}
notif = msg.getValue("link","");
if (m_linkMon && !notif.null()) {
up = msg.getBoolValue(YSTRING("linkup"),false);
sendTrap(lookup(( up ? LinkUp : LinkDown),s_sigNotifs),notif);
if (!up && m_linkInfo)
m_linkInfo->updateAlarmCounter(name);
}
break;
case TRUNK:
if (m_trunkMon)
sendTrap(lookup(( up ? TrunkUp : TrunkDown),s_sigNotifs),name,0,text);
if (!up && m_trunkInfo)
m_trunkInfo->updateAlarmCounter(name);
break;
default:
break;
}
}
// build and send trap from interface notifications
void Monitor::sendCardNotifs(Message& msg)
{
String device = msg.getValue("interface","");
DDebug(this,DebugInfo,"::sendCardNotifs() - a notification from interface '%s' has been received",device.c_str());
if (device.null())
return;
String notif = msg.getValue("notify","");
int type = lookup(notif,s_cardInfo,0);
if (type && m_interfaceMon) {
String trap = lookup(type,s_cardNotifs,"");
if (!trap.null())
sendTrap(notif,device);
if (m_ifaceInfo)
m_ifaceInfo->updateAlarmCounter(device);
}
}
// helper function to check for a value against a threshold from a BaseInfo struct and send a notification if necessary
static void checkInfo(unsigned int count, BaseInfo& info, unsigned int alrm, TokenDict* dict)
{
DDebug(&__plugin,DebugAll,"checkInfo(count=%d, info={threshold=%d,alarm=%s,counter=%d})",
count,info.threshold,String::boolText(info.alarm),info.counter);
info.counter += count;
if (info.threshold && !info.alarm && info.counter >= info.threshold) {
info.alarm = true;
String notif = lookup(alrm,dict,"");
if (!notif.null())
__plugin.sendTrap(notif,String(info.counter));
}
}
// handle module.update messages from SIP and MGCP
void Monitor::checkNotifs(Message& msg, unsigned int type)
{
DDebug(&__plugin,DebugAll,"::checkNotifs() from module='%s'",lookup(type,s_modules,""));
if (type == SIP) {
unsigned int count = msg.getIntValue("failed_auths",0);
checkInfo(count,s_sipInfo.auths,FailedAuths,s_sipNotifs);
count = msg.getIntValue("transaction_timeouts",0);
checkInfo(count,s_sipInfo.transactions,TransactTimedOut,s_sipNotifs);
count = msg.getIntValue("bye_timeouts",0);
checkInfo(count,s_sipInfo.byes,ByesTimedOut,s_sipNotifs);
}
if (type == MGCP) {
unsigned int transTO = msg.getIntValue("tr_timedout",0);
checkInfo(transTO,s_mgcpInfo.transactions,TransactTimedOut,s_mgcpNotifs);
transTO = msg.getIntValue("del_timedout",0);
checkInfo(transTO,s_mgcpInfo.deletes,DeletesTimedOut,s_mgcpNotifs);
if (s_mgcpInfo.gw_monitor) {
if (msg.getValue("mgcp_gw_down"))
sendTrap(lookup(GWTimeout,s_mgcpNotifs,"mgcpGatewayTimedOut"),msg.getValue("mgcp_gw_down"));
if (msg.getValue("mgcp_gw_up"))
sendTrap(lookup(GWUp,s_mgcpNotifs,"mgcpGatewayUp"),msg.getValue("mgcp_gw_up"));
}
}
}
// get SIP/MGCP transaction info
String Monitor::getTransactionsInfo(const String& query, const int who)
{
String result = "";
if (who == SIP) {
int type = lookup(query,s_sipNotifs,0);
switch (type) {
case TransactTimedOut:
result << s_sipInfo.transactions.counter;
return result;
case FailedAuths:
result << s_sipInfo.auths.counter;
return result;
case ByesTimedOut:
result << s_sipInfo.byes.counter;
return result;
default:
break;
}
}
else if (who == MGCP) {
int type = lookup(query,s_mgcpNotifs,0);
switch (type) {
case TransactTimedOut:
result << s_mgcpInfo.transactions.counter;
return result;
case DeletesTimedOut:
result << s_mgcpInfo.deletes.counter;
return result;
default:
break;
}
}
return "";
}
// build a notification message. Increase the alarm counters if the notification was an alarm
void Monitor::sendTrap(const String& trap, const String& value, unsigned int index, const char* text)
{
DDebug(&__plugin,DebugAll,"Monitor::sendtrap(trap='%s',value='%s',index='%d') [%p]",trap.c_str(),value.c_str(),index,this);
Message* msg = new Message("monitor.notify",0,true);
if (m_newTraps)
msg->addParam("notify","specificAlarm");
msg->addParam("notify.0",trap);
msg->addParam("value.0",value);
if (text && m_newTraps) {
msg->addParam("notify.1","alarmText");
msg->addParam("value.1",text);
}
if (index)
msg->addParam("index",String(index));
Engine::enqueue(msg);
}
// build a notification message. Increase the alarm counters if the notification was an alarm
void Monitor::sendTraps(const NamedList& traps)
{
Message* msg = new Message("monitor.notify",0,true);
if (m_newTraps)
msg->addParam("notify","specificAlarm");
msg->copyParams(traps);
Engine::enqueue(msg);
}
// handle a query for a specific monitored value
bool Monitor::solveQuery(Message& msg)
{
XDebug(__plugin.name(),DebugAll,"::solveQuery()");
String query = msg.getValue("name","");
if (query.null())
return false;
int queryWho = lookup(query,s_categories,-1);
String result = "";
unsigned int index = msg.getIntValue("index",0);
DDebug(__plugin.name(),DebugAll,"::solveQuery(query=%s, index=%u)",query.c_str(),index);
switch (queryWho) {
case DATABASE:
if (m_dbInfo)
result = m_dbInfo->getInfo(query,index,s_databaseQuery);
break;
case CALL_MONITOR:
if (m_callMonitor)
m_callMonitor->get(query,index,result);
break;
case ACTIVE_CALLS:
if (m_activeCallsCache)
result = m_activeCallsCache->getInfo(query,index,s_activeCallInfo);
break;
case TRUNKS:
if (m_trunkInfo)
result = m_trunkInfo->getInfo(query,index,m_trunkInfo->s_trunkInfo);
break;
case LINKSETS:
if (m_linksetInfo)
result = m_linksetInfo->getInfo(query,index,m_linksetInfo->s_linksetInfo);
break;
case LINKS:
if (m_linkInfo)
result = m_linkInfo->getInfo(query,index,m_linkInfo->s_linkInfo);
break;
case IFACES:
if (m_ifaceInfo)
result = m_ifaceInfo->getInfo(query,index,m_ifaceInfo->s_ifacesInfo);
break;
case ACCOUNTS:
if (m_accountsInfo)
result = m_accountsInfo->getInfo(query,index,s_accountInfo);
break;
case ENGINE:
if (m_engineInfo)
result = m_engineInfo->getInfo(query,index,s_engineQuery);
break;
case MODULE:
if (m_moduleInfo)
result = m_moduleInfo->getInfo(query,index,s_moduleQuery);
break;
case AUTH_REQUESTS:
if (m_authHandler)
result = m_authHandler->getCount();
break;
case REGISTER_REQUESTS:
if (m_registerHandler)
result = m_registerHandler->getCount();
break;
case RTP:
if (m_rtpInfo)
result = m_rtpInfo->getInfo(query,index);
break;
case SIP:
case MGCP:
result = getTransactionsInfo(query,queryWho);
break;
default:
return false;
}
msg.setParam("value",result);
return true;
}
// verify if a call hasn't hangup because of a gateway timeout. In that case, if the gateway was
// monitored send a notification
void Monitor::handleChanHangup(const String& address, int& code)
{
DDebug(this,DebugInfo,"::handleChanHangup('%s', '%d')",address.c_str(),code);
if (address.null())
return;
if (m_sipMonitoredGws && m_sipMonitoredGws->find(address)) {
if (code == 408 && !m_timedOutGws.find(address)) {
sendTrap(lookup(GWTimeout,s_sipNotifs,"gatewayTimeout"),address);
m_timedOutGws.append(new String(address));
}
}
}
// if a call has passed through, get the gateway and verify if it was previously down
// if it was send a notification that the gateway is up again
bool Monitor::verifyGateway(const String& address)
{
if (address.null())
return false;
if (m_timedOutGws.find(address)) {
m_timedOutGws.remove(address);
sendTrap(lookup(GWUp,s_sipNotifs,"gatewayUp"),address);
}
return true;
}
};
/* vi: set ts=8 sw=4 sts=4 noet: */