1 /**
2 * monitoring.cpp
3 * This file is part of the YATE Project http://YATE.null.ro
4 *
5 * Module for monitoring and gathering information about YATE.
6 *
7 * Yet Another Telephony Engine - a fully featured software PBX and IVR
8 * Copyright (C) 2004-2014 Null Team
9 *
10 * This software is distributed under multiple licenses;
11 * see the COPYING file in the main directory for licensing
12 * information for this specific distribution.
13 *
14 * This use of this software may be subject to additional restrictions.
15 * See the LEGAL file in the main directory for details.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 #include <yatephone.h>
23
24 #define SIP_PORT 5060
25
26 using namespace TelEngine;
27
28 namespace {
29
30 class Monitor;
31 class MsgUpdateHandler;
32 class CdrHandler;
33 class HangupHandler;
34 class CallMonitor;
35
36 // structure to hold a counter, a threshold for the counter
37 // and an alarm for when the threshold had been surpassed
38 typedef struct {
39 unsigned int counter;
40 unsigned int threshold;
41 bool alarm;
42 } BaseInfo;
43
44 // container for MGCP transaction information
45 typedef struct {
46 BaseInfo transactions; // MGCP transactions information
47 BaseInfo deletes; // MGCP delete connection transactions that have timed out
48 u_int64_t reset; // interval after which the data of this structure should be reset
49 u_int64_t resetTime; // time at which the data should be reset
50 bool gw_monitor;
51 } MGCPInfo;
52
53 // container for SIP transaction information
54 typedef struct {
55 BaseInfo auths; // SIP authentication requests information
56 BaseInfo transactions; // SIP timed out transactions information
57 BaseInfo byes; // SIP timed out BYE transactions information
58 u_int64_t reset;
59 u_int64_t resetTime;
60 } SIPInfo;
61
62 /**
63 * Class Cache
64 * BaseClass for retaining and expiring different type of data
65 */
66 class Cache : public Mutex, public GenObject
67 {
68 public:
69 enum Info {
70 COUNT = 1,
71 INDEX = 2,
72 };
Cache(const char * name)73 inline Cache(const char* name)
74 : Mutex(false,name),
75 m_reload(true), m_expireTime(0), m_retainInfoTime(0)
76 { }
77 virtual ~Cache();
78
79 // set the time which is used for increasing the expire time for the cache at each access
setRetainInfoTime(u_int64_t time)80 inline void setRetainInfoTime(u_int64_t time)
81 {
82 m_retainInfoTime = time;
83 m_expireTime = 0; //expire data
84 }
85
86 // get information from the cached data
87 virtual String getInfo(const String& query, unsigned int& index, TokenDict* dict);
88 // check if the information has expired
isExpired()89 inline bool isExpired()
90 { return Time::secNow() > m_expireTime; }
91 // update the time at which the data will expire
updateExpire()92 inline void updateExpire()
93 {
94 m_expireTime = Time::secNow() + m_retainInfoTime;
95 m_reload = false;
96 }
97
98 protected:
99 // load data into this object from a engine.status message
100 virtual bool load();
101 // discard the cached data
102 virtual void discard();
103 // table containing information about modules obtained from an engine.status message
104 ObjList m_table;
105 // flag for reloading
106 bool m_reload;
107 private:
108 // time at which the cached data will expire (in seconds)
109 u_int64_t m_expireTime;
110 // value with which increase the expire time at each access
111 u_int64_t m_retainInfoTime;
112 };
113
114 /**
115 * Class ActiveCallInfo
116 * Hold data about current calls
117 */
118 class ActiveCallsInfo : public Cache
119 {
120 public:
121 enum InfoType {
122 COUNT = 1, // count of current call
123 INDEX = 2, // index of a call
124 ID = 3, // id of a call
125 STATUS = 4, // status of a call
126 CALLER = 5, // caller party
127 CALLED = 6, // called party
128 PEER = 7, // peer(s) channel of a call
129 DURATION = 8, // call duration
130 };
131 // Constructor
ActiveCallsInfo()132 inline ActiveCallsInfo()
133 : Cache("Monitor::activeCallsInfo")
134 { }
135 // Destructor
~ActiveCallsInfo()136 inline ~ActiveCallsInfo()
137 { }
138 // add information about peers by checking the billing ID
139 String checkPeers(const String& billID, const String& callID);
140
141 protected:
142 // load data into this object from a engine.status message
143 bool load();
144 };
145
146 /**
147 * Class SigInfo
148 * Base class for handling information from the signalling channel module about signalling components
149 */
150 class SigInfo : public Cache
151 {
152 public:
153 // enum for types of information
154 enum InfoType {
155 COUNT = 1, // number of components
156 INDEX = 2, // index of a component
157 ID = 3, // id of a component
158 STATUS = 4, // status of a component
159 TYPE = 5, // the type of the component
160 ALARMS_COUNT = 6, // alarm counter for the component
161 SKIP = 7, // helper value to skip unnecessary information when parsing the status string
162 };
163 // Constructor
SigInfo(const char * name,const TokenDict * dict)164 inline SigInfo(const char* name, const TokenDict* dict)
165 : Cache(name), m_dictionary(dict)
166 { }
167 // Destructor
~SigInfo()168 inline ~SigInfo()
169 { m_table.clear(); }
170 // update the alarm counter for the component with the given name
171 void updateAlarmCounter(const String& name);
172 protected:
173 // load data into this object from a engine.status message
174 virtual bool load();
175 virtual void discard();
176 // dictionary for
177 const TokenDict* m_dictionary;
178 };
179
180 /**
181 * Class InterfaceInfo
182 */
183 class InterfaceInfo : public SigInfo
184 {
185 public:
186 // Constructor
InterfaceInfo()187 inline InterfaceInfo()
188 : SigInfo("Monitor::ifaceInfo",s_ifacesInfo)
189 { }
190 // Destructor
~InterfaceInfo()191 inline ~InterfaceInfo()
192 { }
193 // Dictionary for mapping Monitor queries about signalling interfaces
194 static TokenDict s_ifacesInfo[];
195 protected:
196 // load data into this object from a engine.status message
197 bool load();
198 };
199
200 /**
201 * Class LinkInfo
202 */
203 class LinkInfo : public SigInfo
204 {
205 public:
206 enum LinkExtraInfo {
207 UPTIME = 8,
208 };
209 // Constructor
LinkInfo()210 inline LinkInfo()
211 : SigInfo("Monitor::linkInfo",s_linkInfo)
212 { }
213 // Destructor
~LinkInfo()214 inline ~LinkInfo()
215 { }
216 // dictionary for mapping Monitor queries
217 static TokenDict s_linkInfo[];
218 protected:
219 // load data into this object from a engine.status message
220 bool load();
221 };
222
223 /**
224 * Class LinksetInfo
225 * Hold status data about signalling linksets
226 */
227 class LinksetInfo : public SigInfo
228 {
229 public:
LinksetInfo()230 inline LinksetInfo()
231 : SigInfo("Monitor::linksetInfo",s_linksetInfo)
232 { }
~LinksetInfo()233 inline ~LinksetInfo()
234 { }
235 // dictionary for mapping Monitor queries
236 static TokenDict s_linksetInfo[];
237 protected:
238 // load data into this object from a engine.status message
239 bool load();
240 // parse individual link entries
241 NamedList* parseLinksetInfo(String& info, const String& link, NamedList* infoFill = 0);
242 // dictonary for mapping status parameters
243 static TokenDict s_linksetStatus[];
244 };
245
246 /**
247 * Class TrunkInfo
248 * Hold status data about signalling trunks
249 */
250 class TrunkInfo : public SigInfo
251 {
252 public:
253 enum TrunkExtraInfo {
254 CIRCUITS = 7,
255 CALLS = 8,
256 LOCKED = 9,
257 IDLE = 10,
258 };
259 // Constructor
TrunkInfo()260 inline TrunkInfo()
261 : SigInfo("Monitor::trunkInfo",s_trunkInfo)
262 { }
263 // Destructor
~TrunkInfo()264 inline ~TrunkInfo()
265 { }
266 // dictionary for mapping Monitor queries
267 static TokenDict s_trunkInfo[];
268 protected:
269 // load data into this object from a engine.status message
270 bool load();
271 virtual void discard();
272 // parse individual trunk entries
273 NamedList* parseTrunkInfo(String& info,const String& trunkName, NamedList* infoFill = 0);
274 // dictonary for mapping status parameters
275 static TokenDict s_trunkStatus[];
276 };
277
278 /**
279 * Accounts Info
280 * Cache for account status information
281 */
282 class AccountsInfo : public Cache
283 {
284 public:
285 // account information type
286 enum AccountInfoType {
287 COUNT = 1,
288 INDEX = 2,
289 ID = 3,
290 STATUS = 4,
291 PROTO = 5,
292 USERNAME = 6,
293 };
294 // Constructor
AccountsInfo()295 inline AccountsInfo()
296 : Cache("Monitor::accountsInfo")
297 { }
298 // Destructor
~AccountsInfo()299 inline ~AccountsInfo()
300 { }
301 private:
302 // load data into this object from a engine.status message
303 bool load();
304 };
305
306 /**
307 * EngineInfo - engine status information cache
308 */
309 class EngineInfo : public Cache
310 {
311 public:
312 enum EngineInfoType {
313 ENGINE_TYPE = 1,
314 ENGINE_PLUGINS = 2,
315 ENGINE_HANDLERS = 3,
316 ENGINE_MESSAGES = 4,
317 ENGINE_THREADS = 5,
318 ENGINE_WORKERS = 6,
319 ENGINE_MUTEXES = 7,
320 ENGINE_LOCKS = 8,
321 ENGINE_SEMAPHORES = 9,
322 ENGINE_WAITING = 10,
323 ENGINE_RUNATTEMPT = 11,
324 ENGINE_NODENAME = 12,
325 ENGINE_STATE = 13,
326 ENGINE_CALL_ACCEPT = 14,
327 ENGINE_UNEX_RESTART = 15,
328 ENGINE_MAXQUEUED = 16,
329 ENGINE_MSGRATE = 17,
330 ENGINE_MAXMSGRATE = 18,
331 ENGINE_MSGENQUEUED = 19,
332 ENGINE_MSGDEQUEUED = 20,
333 ENGINE_MSGDISPATCHED = 21,
334 };
335 // Constructor
EngineInfo()336 inline EngineInfo()
337 : Cache("Monitor::engineInfo")
338 { }
339 // Destructor
~EngineInfo()340 inline ~EngineInfo()
341 { }
342 // get information from the cached data. Reimplemented from Cache
343 String getInfo(const String query, unsigned int index, TokenDict* dict);
344 private:
345 // load data into this object from a engine.status message
346 bool load();
347 // dictionary for mapping engine status parameters
348 static TokenDict s_engineInfo[];
349 };
350
351 /**
352 * ModuleInfo - module information cache
353 */
354 class ModuleInfo : public Cache
355 {
356 public:
357 enum ModuleInfoType {
358 COUNT = 1,
359 INDEX = 2,
360 MODULE_NAME = 3,
361 MODULE_TYPE = 4,
362 MODULE_INFO = 5,
363 MODULE_FORMAT = 6,
364 };
365 // Constructor
ModuleInfo()366 inline ModuleInfo()
367 : Cache("Monitor::moduleInfo")
368 { }
369 // Destructor
~ModuleInfo()370 inline ~ModuleInfo()
371 { }
372 private:
373 // load data into this object from a engine.status message
374 bool load();
375 // dictionary for mapping engine status parameters
376 static TokenDict s_moduleInfo[];
377 };
378
379 /**
380 * DatabaseAccount
381 * A container which holds status information about a single database account
382 */
383 class DatabaseAccount : public GenObject
384 {
385 public:
386 enum DbIndex {
387 TOTAL_IDX = 0, // index for total number of queries
388 FAILED_IDX = 1, // index for number of failed queries
389 ERROR_IDX = 2, // index for number of queries returning with an error status
390 TIME_IDX = 3, // index for time spent executing queries
391 CONN_IDX = 4, // index for number of active connections
392 };
393 enum DbAlarms {
394 TOTAL_ALARM = 0x1,
395 FAILED_ALARM = 0x2,
396 ERROR_ALARM = 0x4,
397 EXEC_ALARM = 0x8,
398 CONNS_ALARM = 0x10,
399 };
400 // enum for information type
401 enum DbData {
402 QueriesCount = 1,
403 FailedCount = 2,
404 ErrorsCount = 3,
405 ExecTime = 4,
406 TooManyAlrm = 5,
407 TooManyFailedAlrm = 6,
408 TooManyErrorAlrm = 7,
409 ExecTooLongAlrm = 8,
410 NoConnAlrm = 9,
411 TooManyAlrmCount = 10,
412 TooManyFailedAlrmCount = 11,
413 TooManyErrorAlrmCount = 12,
414 ExecTooLongAlrmCount = 13,
415 NoConnAlrmCount = 14,
416 MaxQueries = 15,
417 MaxFailedQueries = 16,
418 MaxErrorQueries = 17,
419 MaxExecTime = 18,
420 AccountName = 19,
421 AccountIndex = 20,
422 };
423 // Constructor
424 DatabaseAccount(const NamedList* cfg);
425 // Destructor
~DatabaseAccount()426 inline ~DatabaseAccount()
427 { }
428 // reimplemented toString() method to make object searcheble in lists
toString() const429 inline const String& toString() const
430 { return m_name; }
431 // set the database entry index in the database account table
setIndex(unsigned int index)432 inline void setIndex(unsigned int index)
433 { m_index = index; }
434 // get this account's index
index()435 inline unsigned int index()
436 { return m_index; }
437 // update the internal data from the list received
438 void update(const NamedList& info);
439 // obtain data
440 const String getInfo(unsigned int query);
441 // reset internal data
442 void reset();
isCurrent()443 inline bool isCurrent()
444 { return m_isCurrent; }
setIsCurrent(bool current=true)445 inline void setIsCurrent(bool current = true)
446 { m_isCurrent = current; }
447 // update configuration for this direction
448 void updateConfig(const NamedList* cfg);
449 private:
450 // account name
451 String m_name;
452 // index
453 unsigned int m_index;
454 // counters for number of queries
455 unsigned int m_dbCounters[ExecTime];
456 // counters for previous interval counter values
457 unsigned int m_prevDbCounters[ExecTime];
458 // alarms set
459 u_int16_t m_alarms;
460 // alarm counters
461 unsigned int m_alarmCounters[CONN_IDX + 1];
462 // thresholds for triggering alarms
463 unsigned int m_thresholds[CONN_IDX];
464 // time at which internal data should be reset
465 unsigned int m_resetTime;
466 // time to hold on on current data
467 unsigned int m_resetInterval;
468 // flag if monitored account is current (i.e. false means this direction was removed from monitoring)
469 bool m_isCurrent;
470 };
471
472 /**
473 * DatabaseInfo - database information
474 * A list of DatabaseAccounts
475 */
476 class DatabaseInfo : public Cache
477 {
478 public:
479 enum DbInfoType {
480 Connections = 1,
481 FailedConns = 2,
482 Accounts = 3,
483 };
484 // Constructor
DatabaseInfo(bool monitored=false)485 inline DatabaseInfo(bool monitored = false)
486 : Cache("Monitor::dbInfo"), m_monitor(monitored)
487 { }
488 // Destructor
~DatabaseInfo()489 inline ~DatabaseInfo()
490 { m_table.clear(); }
491 // create and add a new DatabaseAccount created from the received configuration
492 void addDatabase(NamedList* cfg);
493 // update the internal data from the received message
494 void update(const Message& msg);
495 // get the requested information
496 String getInfo(const String& query, unsigned int& index, TokenDict* dict);
497 // try to reset internal data of the table entries
498 void reset();
499
setMonitorEnabled(bool enable=false)500 inline void setMonitorEnabled(bool enable = false)
501 { m_monitor = enable; }
monitorEnable()502 inline bool monitorEnable()
503 { return m_monitor; }
504 // reconfigure
505 void setConfigure(const NamedList* sect);
506 // update database account after reinitialization
507 void updateDatabaseAccounts();
508 private:
509 static TokenDict s_databaseInfo[];
510 bool load();
511 // number of successful and failed connections to databases
512 unsigned int m_connData[FailedConns];
513 bool m_monitor;
514
515 static String s_dbParam;
516 static String s_totalParam;
517 static String s_failedParam;
518 static String s_errorParam;
519 static String s_hasConnParam;
520 static String s_timeParam;
521 };
522
523 /**
524 * RTPEntry. Container holding data about a single monitored RTP direction
525 */
526 class RTPEntry : public GenObject
527 {
528 public:
529 enum RTPInfoType {
530 Count = 1,
531 Index = 2,
532 Direction = 3,
533 NoAudio = 4,
534 LostAudio = 5,
535 PktsLost = 6,
536 SyncLost = 7,
537 SeqLost = 8,
538 WrongSRC = 9,
539 WrongSSRC = 10,
540 };
541 RTPEntry(String rtpDirection);
542 ~RTPEntry();
toString() const543 inline const String& toString() const
544 { return m_rtpDir; }
545 // update the entry from the received information
546 void update(const NamedList& nl);
547 // reset internal data
548 void reset();
549 // set the index for this entry
setIndex(unsigned int index)550 inline void setIndex(unsigned int index)
551 { m_index = index; }
552 // get info from thins entry
553 String getInfo(unsigned int query);
554 // mapping dictionary for Monitor queries
555 static TokenDict s_rtpInfo[];
isCurrent()556 inline bool isCurrent()
557 { return m_isCurrent; }
setIsCurrent(bool current=true)558 inline void setIsCurrent(bool current = true)
559 { m_isCurrent = current; }
560 private:
561 // the RTP direction
562 String m_rtpDir;
563 // counters
564 unsigned int m_counters[WrongSSRC - Direction];
565 unsigned int m_index;
566 // flag if monitored direction is current (i.e. false means this direction was removed from monitoring)
567 bool m_isCurrent;
568 };
569
570 /**
571 * RTPTable. A list of RTPEntry
572 */
573 class RTPTable : public GenObject
574 {
575 public:
576 // Constructor
577 inline RTPTable(const NamedList* cfg);
578 // Destructor
~RTPTable()579 inline ~RTPTable()
580 { m_rtpEntries.clear(); }
581 // update internal data
582 void update(Message& msg);
583 // get the answer to a query
584 String getInfo(const String& query, const unsigned int& index);
585 // reset internal data
586 void reset();
587 // check if the internal data should be reset
shouldReset()588 inline bool shouldReset()
589 { return Time::secNow() >= m_resetTime; }
590 void reconfigure(const NamedList* cfg);
591 private:
592 // list of RTPEntry
593 ObjList m_rtpEntries;
594 Mutex m_rtpMtx;
595 // interval for how long the data should be kept
596 u_int64_t m_resetInterval;
597 // time at which the data should be reset
598 u_int64_t m_resetTime;
599 // RTP monitored?
600 bool m_monitor;
601 };
602
603 // A route entry which is monitored for quality of service values
604 class CallRouteQoS : public GenObject
605 {
606 public:
607 enum CallStatus {
608 ANSWERED = 1,
609 DELIVERED = 2,
610 };
611 enum Indexes {
612 CURRENT_IDX = 0,
613 PREVIOUS_IDX = 1,
614 TOTAL_IDX = 2,
615 };
616 enum ALARMS {
617 LOW_ASR = 1,
618 HIGH_ASR = 2,
619 LOW_NER = 4,
620 };
621 enum QoSNotifs {
622 ASR_LOW = 1,
623 ASR_HIGH = 2,
624 ASR_LOW_ALL = 3,
625 ASR_HIGH_ALL = 4,
626 NER_LOW = 5,
627 NER_LOW_ALL = 6,
628 ASR = 7,
629 NER = 8,
630 ASR_ALL = 9,
631 NER_ALL = 10,
632 MIN_ASR = 11,
633 MAX_ASR = 12,
634 MIN_NER = 13,
635 LOW_ASR_COUNT = 14,
636 HIGH_ASR_COUNT = 15,
637 LOW_ASR_ALL_COUNT = 16,
638 HIGH_ASR_ALL_COUNT = 17,
639 LOW_NER_COUNT = 18,
640 LOW_NER_ALL_COUNT = 19,
641 HANGUP = 40,
642 REJECT = 41,
643 BUSY = 42,
644 CANCELLED = 43,
645 NO_ANSWER = 44,
646 NO_ROUTE = 45,
647 NO_CONN = 46,
648 NO_AUTH = 47,
649 CONGESTION = 48,
650 NO_MEDIA = 49,
651 NO_CAUSE = 50,
652 HANGUP_ALL = 60,
653 REJECT_ALL = 61,
654 BUSY_ALL = 62,
655 CANCELLED_ALL = 63,
656 NO_ANSWER_ALL = 64,
657 NO_ROUTE_ALL = 65,
658 NO_CONN_ALL = 66,
659 NO_AUTH_ALL = 67,
660 CONGESTION_ALL = 68,
661 NO_MEDIA_ALL = 69,
662 NAME = 80,
663 INDEX = 81,
664 };
665 // Constructor
666 CallRouteQoS(const String direction, const NamedList* cfg = 0);
667 // Destructor
668 ~CallRouteQoS();
669 // update the call counters and call end reason counters
670 void update(int type = -1, int endReason = -1);
671 // update the ASR and NER values
672 void updateQoS();
673 // reset the internal data
674 void reset();
675 // check if the given value has surpassed the given threshold and set the appropriate alarm
676 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);
677 // is this route in a state of alarm
678 bool alarm();
679 // get the alarm
680 const String alarmText();
681 // send periodic notifications
682 void sendNotifs(unsigned int index, bool reset = false);
683 // get the response to the given query
684 bool get(int query, String& result);
685 // reimplemented toString() method to make the object searcheable in lists
toString() const686 inline const String& toString() const
687 { return m_routeName; }
688 // set the index of this object
setIndex(unsigned int index)689 inline void setIndex(unsigned int index)
690 { m_index = index; }
index()691 inline unsigned int index()
692 { return m_index; }
isCurrent()693 inline bool isCurrent()
694 { return m_isCurrent; }
setIsCurrent(bool current=true)695 inline void setIsCurrent(bool current = true)
696 { m_isCurrent = current; }
697 // update configuration for this direction
698 void updateConfig(const NamedList* cfg);
699 private:
700 String m_routeName;
701 // call hangup reasons counters
702 unsigned int m_callCounters[NO_CAUSE - HANGUP];
703 unsigned int m_callCountersAll[NO_CAUSE - HANGUP];
704
705 unsigned int m_totalCalls[TOTAL_IDX + 1]; // total calls
706 unsigned int m_answeredCalls[TOTAL_IDX + 1]; // total answered calls
707 unsigned int m_delivCalls[TOTAL_IDX + 1]; // total delivered calls
708
709 // alarm flags for avoiding sending multiple times the same alarm
710 u_int8_t m_alarms;
711 u_int8_t m_overallAlarms;
712
713 // flags for keeping track if an alarm has been sent or not
714 u_int8_t m_alarmsSent;
715 u_int8_t m_overallAlarmsSent;
716
717 // alarm thresholds
718 int m_minASR;
719 int m_maxASR;
720 int m_minNER;
721
722 // alarm counters
723 unsigned int m_alarmCounters[NER_LOW_ALL + 1];
724 // minimum number of calls before starting
725 unsigned int m_minCalls;
726 // index in the table
727 unsigned int m_index;
728 // flag if monitored direction is current (i.e. false means this direction was removed from monitoring)
729 bool m_isCurrent;
730 };
731
732 /**
733 * Class CallMonitor
734 * Monitors number of calls, termination causes, computes statistic data
735 */
736 class CallMonitor : public MessageHandler, public Thread
737 {
738 public:
739 enum Indexes {
740 IN_ASR_Idx = 0,
741 OUT_ASR_Idx = 1,
742 IN_NER_Idx = 2,
743 OUT_NER_Idx = 3,
744 };
745
746 enum Queries {
747 INCOMING_CALLS = 9,
748 OUTGOING_CALLS = 10,
749 ROUTES_COUNT = 11,
750 };
751
752 // constructor
753 CallMonitor(const NamedList* sect, unsigned int priority = 100);
~CallMonitor()754 virtual ~CallMonitor()
755 { }
756 // inherited methods
757 virtual bool received(Message& msg);
758 virtual bool init();
759 virtual void run();
760
761 // obtain the value from the monitored data
762 void get(const String& query, const int& index, String& result);
763
764 // get the value of a call counter
765 bool getCounter(int type, unsigned int& value);
766
767 // send an alarm from a route
768 void sendAlarmFrom(CallRouteQoS* route);
769
770 // add a route to be monitored
771 void addRoute(NamedList* sect);
772
773 // reconfigure
774 void setConfigure(const NamedList* sect);
775 // update route after reinitialization
776 void updateRoutes();
777
778 private:
779 // interval at which notifications are sent
780 unsigned int m_checkTime;
781 // time at which notifications are sent
782 unsigned int m_notifTime;
783 // call counters
784 unsigned int m_inCalls;
785 unsigned int m_outCalls;
786 // list of routes
787 ObjList m_routes;
788 Mutex m_routesMtx;
789 bool m_first;
790 // parameter on which to select routes from call.cdr message
791 String m_routeParam;
792 // Directions monitored?
793 bool m_monitor;
794 // configure mutex
795 Mutex m_cfgMtx;
796 };
797
798 class SnmpMsgHandler;
799 class EngineStartHandler;
800 class AuthHandler;
801 class RegisterHandler;
802
803 /**
804 * Class Monitor
805 * Monitoring module
806 */
807 class Monitor : public Module
808 {
809 public:
810 // enum for notification categories
811 enum Categories {
812 CALL_MONITOR = 1,
813 DATABASE = 2,
814 ALARM_COUNTERS = 3,
815 ACTIVE_CALLS = 4,
816 PSTN = 5,
817 ENGINE = 6,
818 MODULE = 7,
819 AUTH_REQUESTS = 8,
820 REGISTER_REQUESTS = 9,
821 INTERFACE = 10,
822 SIP = 11,
823 RTP = 12,
824 TRUNKS = 13,
825 LINKSETS = 14,
826 LINKS = 15,
827 IFACES = 16,
828 ACCOUNTS = 17,
829 MGCP = 18,
830 };
831
832 enum SigTypes {
833 SS7_MTP3 = 1,
834 TRUNK = 2,
835 ISDN = 3,
836 };
837
838 enum Cards {
839 InterfaceDown = 1,
840 InterfaceUp,
841 };
842
843 enum SigNotifs {
844 TrunkDown = 1,
845 TrunkUp,
846 LinksetDown,
847 LinksetUp,
848 LinkDown,
849 LinkUp,
850 IsdnQ921Down,
851 IsdnQ921Up,
852 };
853
854 enum SipNotifs {
855 TransactTimedOut = 1,
856 FailedAuths,
857 ByesTimedOut,
858 GWTimeout,
859 GWUp,
860 DeletesTimedOut,
861 };
862 Monitor();
863 virtual ~Monitor();
864 //inherited methods
865 virtual void initialize();
866 virtual bool received(Message& msg, int id);
867 bool unload();
868
869 // handle module.update messages
870 void update(Message& msg);
871 // read configuration file
872 void readConfig(const Configuration& cfg);
873 // build and send SS7 notifications
874 void sendSigNotifs(Message& msg);
875 // build and send physical interface notifications
876 void sendCardNotifs(Message& msg);
877 // handle MGCP & SIP status notifications
878 void checkNotifs(Message& msg, unsigned int type);
879 // build a notification message
880 void sendTrap(const String& trap, const String& value, unsigned int index = 0,
881 const char* text = 0);
882 // send multiple notifications at once
883 void sendTraps(const NamedList& traps);
884 // handle a monitor.query message
885 bool solveQuery(Message& msg);
886 // update monitored SIP gateway information
887 void handleChanHangup(const String& address, int& cause);
888 bool verifyGateway(const String& address);
889 // obtain SIP/MGCP transactions info
890 String getTransactionsInfo(const String& query, const int who);
891 private:
892 // message handlers
893 MsgUpdateHandler* m_msgUpdateHandler;
894 SnmpMsgHandler* m_snmpMsgHandler;
895 HangupHandler* m_hangupHandler;
896 EngineStartHandler* m_startHandler;
897 CallMonitor* m_callMonitor;
898 AuthHandler* m_authHandler;
899 RegisterHandler* m_registerHandler;
900 bool m_init;
901 bool m_newTraps;
902 // list of monitored SIP gateways and timed out gateways
903 ObjList* m_sipMonitoredGws;
904 ObjList m_timedOutGws;
905
906 // flags if certain monitored information should be passed along in form of notifications
907 bool m_trunkMon;
908 bool m_linksetMon;
909 bool m_linkMon;
910 bool m_interfaceMon;
911 bool m_isdnMon;
912 // caches
913 ActiveCallsInfo* m_activeCallsCache;
914 TrunkInfo* m_trunkInfo;
915 EngineInfo* m_engineInfo;
916 ModuleInfo* m_moduleInfo;
917 DatabaseInfo* m_dbInfo;
918 RTPTable* m_rtpInfo;
919
920 LinksetInfo* m_linksetInfo;
921 LinkInfo* m_linkInfo;
922 InterfaceInfo* m_ifaceInfo;
923 AccountsInfo* m_accountsInfo;
924 };
925
926 static int s_yateRun = 0;
927 static int s_yateRunAlarm = 0;
928 static int s_alarmThreshold = DebugNote;
929 static String s_nodeState = "";
930 static double s_qosHysteresisFactor = 2.0;
931
932 MGCPInfo s_mgcpInfo = { {0, 0, false}, {0, 0, false}, 0, 0, false};
933 static SIPInfo s_sipInfo = { {0, 0, false}, {0, 0, false}, {0, 0, false}, 0, 0};
934
935 static TokenDict s_modules[] = {
936 {"mysqldb", Monitor::DATABASE},
937 {"pgsqldb", Monitor::DATABASE},
938 {"sig", Monitor::PSTN},
939 {"wanpipe", Monitor::INTERFACE},
940 {"zaptel", Monitor::INTERFACE},
941 {"Tdm", Monitor::INTERFACE},
942 {"sip", Monitor::SIP},
943 {"yrtp", Monitor::RTP},
944 {"mgcpca", Monitor::MGCP},
945 {0,0}
946 };
947
948 static TokenDict s_categories[] = {
949 // database info
950 {"databaseCount", Monitor::DATABASE},
951 {"databaseIndex", Monitor::DATABASE},
952 {"databaseAccount", Monitor::DATABASE},
953 {"queriesCount", Monitor::DATABASE},
954 {"failedQueries", Monitor::DATABASE},
955 {"errorQueries", Monitor::DATABASE},
956 {"queryExecTime", Monitor::DATABASE},
957 {"successfulConnections", Monitor::DATABASE},
958 {"failedConnections", Monitor::DATABASE},
959 // database alarm counters
960 {"tooManyQueriesAlarms", Monitor::DATABASE},
961 {"tooManyFailedQueriesAlarms", Monitor::DATABASE},
962 {"tooManyErrorQueriesAlarms", Monitor::DATABASE},
963 {"queryExecTooLongAlarms", Monitor::DATABASE},
964 {"noConnectionAlarms", Monitor::DATABASE},
965 // database thresholds
966 {"queriesCountThreshold", Monitor::DATABASE},
967 {"failedQueriesThreshold", Monitor::DATABASE},
968 {"errorQueriesThreshold", Monitor::DATABASE},
969 {"queryExecTimeThreshold", Monitor::DATABASE},
970 // QOS
971 {"qosDirectionsCount", Monitor::CALL_MONITOR},
972 {"qosEntryIndex", Monitor::CALL_MONITOR},
973 {"qosEntryDirection", Monitor::CALL_MONITOR},
974 {"lowASRThreshold", Monitor::CALL_MONITOR},
975 {"highASRThreshold", Monitor::CALL_MONITOR},
976 {"currentASR", Monitor::CALL_MONITOR},
977 {"overallASR", Monitor::CALL_MONITOR},
978 {"lowNERThreshold", Monitor::CALL_MONITOR},
979 {"currentNER", Monitor::CALL_MONITOR},
980 {"overallNER", Monitor::CALL_MONITOR},
981 // QOS alarm counters
982 {"currentLowASRAlarmCount", Monitor::CALL_MONITOR},
983 {"overallLowASRAlarmCount", Monitor::CALL_MONITOR},
984 {"currentHighASRAlarmCount", Monitor::CALL_MONITOR},
985 {"overallHighASRAlarmCount", Monitor::CALL_MONITOR},
986 {"currentLowNERAlarmCount", Monitor::CALL_MONITOR},
987 {"overallLowNERAlarmCount", Monitor::CALL_MONITOR},
988 // call counters
989 {"incomingCalls", Monitor::CALL_MONITOR},
990 {"outgoingCalls", Monitor::CALL_MONITOR},
991
992 {"currentHangupEndCause", Monitor::CALL_MONITOR},
993 {"currentBusyEndCause", Monitor::CALL_MONITOR},
994 {"currentRejectedEndCause", Monitor::CALL_MONITOR},
995 {"currentCancelledEndCause", Monitor::CALL_MONITOR},
996 {"currentNoAnswerEndCause", Monitor::CALL_MONITOR},
997 {"currentNoRouteEndCause", Monitor::CALL_MONITOR},
998 {"currentNoConnectionEndCause", Monitor::CALL_MONITOR},
999 {"currentNoAuthEndCause", Monitor::CALL_MONITOR},
1000 {"currentCongestionEndCause", Monitor::CALL_MONITOR},
1001 {"currentNoMediaEndCause", Monitor::CALL_MONITOR},
1002
1003 {"overallHangupEndCause", Monitor::CALL_MONITOR},
1004 {"overallBusyEndCause", Monitor::CALL_MONITOR},
1005 {"overallRejectedEndCause", Monitor::CALL_MONITOR},
1006 {"overallCancelledEndCause", Monitor::CALL_MONITOR},
1007 {"overallNoAnswerEndCause", Monitor::CALL_MONITOR},
1008 {"overallNoRouteEndCause", Monitor::CALL_MONITOR},
1009 {"overallNoConnectionEndCause", Monitor::CALL_MONITOR},
1010 {"overallNoAuthEndCause", Monitor::CALL_MONITOR},
1011 {"overallCongestionEndCause", Monitor::CALL_MONITOR},
1012 {"overallNoMediaEndCause", Monitor::CALL_MONITOR},
1013
1014 // connections info
1015 // linksets
1016 {"linksetCount", Monitor::LINKSETS},
1017 {"linksetIndex", Monitor::LINKSETS},
1018 {"linksetID", Monitor::LINKSETS},
1019 {"linksetType", Monitor::LINKSETS},
1020 {"linksetStatus", Monitor::LINKSETS},
1021 {"linksetDownAlarms", Monitor::LINKSETS},
1022 // links
1023 {"linkCount", Monitor::LINKS},
1024 {"linkIndex", Monitor::LINKS},
1025 {"linkID", Monitor::LINKS},
1026 {"linkType", Monitor::LINKS},
1027 {"linkStatus", Monitor::LINKS},
1028 {"linkDownAlarms", Monitor::LINKS},
1029 {"linkUptime", Monitor::LINKS},
1030 // interfaces
1031 {"interfacesCount", Monitor::IFACES},
1032 {"interfaceIndex", Monitor::IFACES},
1033 {"interfaceID", Monitor::IFACES},
1034 {"interfaceStatus", Monitor::IFACES},
1035 {"interfaceDownAlarms", Monitor::IFACES},
1036 // accounts
1037 {"accountsCount", Monitor::ACCOUNTS},
1038 {"accountIndex", Monitor::ACCOUNTS},
1039 {"accountID", Monitor::ACCOUNTS},
1040 {"accountStatus", Monitor::ACCOUNTS},
1041 {"accountProtocol", Monitor::ACCOUNTS},
1042 {"accountUsername", Monitor::ACCOUNTS},
1043 // active calls info
1044 {"activeCallsCount", Monitor::ACTIVE_CALLS},
1045 {"callEntryIndex", Monitor::ACTIVE_CALLS},
1046 {"callEntryID", Monitor::ACTIVE_CALLS},
1047 {"callEntryStatus", Monitor::ACTIVE_CALLS},
1048 {"callEntryCaller", Monitor::ACTIVE_CALLS},
1049 {"callEntryCalled", Monitor::ACTIVE_CALLS},
1050 {"callEntryPeerChan", Monitor::ACTIVE_CALLS},
1051 {"callEntryDuration", Monitor::ACTIVE_CALLS},
1052 // trunk info
1053 {"trunksCount", Monitor::TRUNKS},
1054 {"trunkIndex", Monitor::TRUNKS},
1055 {"trunkID", Monitor::TRUNKS},
1056 {"trunkType", Monitor::TRUNKS},
1057 {"trunkCircuitCount", Monitor::TRUNKS},
1058 {"trunkCurrentCallsCount", Monitor::TRUNKS},
1059 {"trunkDownAlarms", Monitor::TRUNKS},
1060 {"trunkCircuitsLocked", Monitor::TRUNKS},
1061 {"trunkCircuitsIdle", Monitor::TRUNKS},
1062 // engine info
1063 {"plugins", Monitor::ENGINE},
1064 {"handlers", Monitor::ENGINE},
1065 {"messages", Monitor::ENGINE},
1066 {"msgMaxQueued", Monitor::ENGINE},
1067 {"msgLastSecond", Monitor::ENGINE},
1068 {"msgMaxPerSecond", Monitor::ENGINE},
1069 {"msgEnqueued", Monitor::ENGINE},
1070 {"msgDequeued", Monitor::ENGINE},
1071 {"msgDispatched", Monitor::ENGINE},
1072 {"threads", Monitor::ENGINE},
1073 {"workers", Monitor::ENGINE},
1074 {"mutexes", Monitor::ENGINE},
1075 {"locks", Monitor::ENGINE},
1076 {"semaphores", Monitor::ENGINE},
1077 {"waitingSemaphores", Monitor::ENGINE},
1078 {"acceptStatus", Monitor::ENGINE},
1079 {"unexpectedRestart", Monitor::ENGINE},
1080 // node info
1081 {"runAttempt", Monitor::ENGINE},
1082 {"name", Monitor::ENGINE},
1083 {"state", Monitor::ENGINE},
1084 // module info
1085 {"moduleCount", Monitor::MODULE},
1086 {"moduleIndex", Monitor::MODULE},
1087 {"moduleName", Monitor::MODULE},
1088 {"moduleType", Monitor::MODULE},
1089 {"moduleExtra", Monitor::MODULE},
1090 // request stats
1091 {"authenticationRequests", Monitor::AUTH_REQUESTS},
1092 {"registerRequests", Monitor::REGISTER_REQUESTS},
1093 // rtp stats
1094 {"rtpDirectionsCount", Monitor::RTP},
1095 {"rtpEntryIndex", Monitor::RTP},
1096 {"rtpDirection", Monitor::RTP},
1097 {"noAudioCounter", Monitor::RTP},
1098 {"lostAudioCounter", Monitor::RTP},
1099 {"packetsLost", Monitor::RTP},
1100 {"syncLost", Monitor::RTP},
1101 {"sequenceNumberLost", Monitor::RTP},
1102 {"wrongSRC", Monitor::RTP},
1103 {"wrongSSRC", Monitor::RTP},
1104 // sip stats
1105 {"transactionsTimedOut", Monitor::SIP},
1106 {"failedAuths", Monitor::SIP},
1107 {"byesTimedOut", Monitor::SIP},
1108 // mgcp stats
1109 {"mgcpTransactionsTimedOut", Monitor::MGCP},
1110 {"deleteTransactionsTimedOut", Monitor::MGCP},
1111 {0,0}
1112 };
1113
1114 static TokenDict s_callQualityQueries[] = {
1115 // alarms
1116 {"currentLowASR", CallRouteQoS::ASR_LOW},
1117 {"overallLowASR", CallRouteQoS::ASR_LOW_ALL},
1118 {"currentHighASR", CallRouteQoS::ASR_HIGH},
1119 {"overallHighASR", CallRouteQoS::ASR_HIGH_ALL},
1120 {"currentLowNER", CallRouteQoS::NER_LOW},
1121 {"overallLowNER", CallRouteQoS::NER_LOW_ALL},
1122 {"qosEntryDirection", CallRouteQoS::NAME},
1123 {"qosEntryIndex", CallRouteQoS::INDEX},
1124 // notifications
1125 {"currentASR", CallRouteQoS::ASR},
1126 {"overallASR", CallRouteQoS::ASR_ALL},
1127 {"currentNER", CallRouteQoS::NER},
1128 {"overallNER", CallRouteQoS::NER_ALL},
1129 // end cause counters
1130 {"currentHangupEndCause", CallRouteQoS::HANGUP},
1131 {"currentBusyEndCause", CallRouteQoS::BUSY},
1132 {"currentRejectedEndCause", CallRouteQoS::REJECT},
1133 {"currentCancelledEndCause", CallRouteQoS::CANCELLED},
1134 {"currentNoAnswerEndCause", CallRouteQoS::NO_ANSWER},
1135 {"currentNoRouteEndCause", CallRouteQoS::NO_ROUTE},
1136 {"currentNoConnectionEndCause", CallRouteQoS::NO_CONN},
1137 {"currentNoAuthEndCause", CallRouteQoS::NO_AUTH},
1138 {"currentCongestionEndCause", CallRouteQoS::CONGESTION},
1139 {"currentNoMediaEndCause", CallRouteQoS::NO_MEDIA},
1140
1141 {"overallHangupEndCause", CallRouteQoS::HANGUP_ALL},
1142 {"overallBusyEndCause", CallRouteQoS::BUSY_ALL},
1143 {"overallRejectedEndCause", CallRouteQoS::REJECT_ALL},
1144 {"overallCancelledEndCause", CallRouteQoS::CANCELLED_ALL},
1145 {"overallNoAnswerEndCause", CallRouteQoS::NO_ANSWER_ALL},
1146 {"overallNoRouteEndCause", CallRouteQoS::NO_ROUTE_ALL},
1147 {"overallNoConnectionEndCause", CallRouteQoS::NO_CONN_ALL},
1148 {"overallNoAuthEndCause", CallRouteQoS::NO_AUTH_ALL},
1149 {"overallCongestionEndCause", CallRouteQoS::CONGESTION_ALL},
1150 {"overallNoMediaEndCause", CallRouteQoS::NO_MEDIA_ALL},
1151 // thresholds
1152 {"lowASRThreshold", CallRouteQoS::MIN_ASR},
1153 {"highASRThreshold", CallRouteQoS::MAX_ASR},
1154 {"lowNERThreshold", CallRouteQoS::MIN_NER},
1155 // alarm counters
1156 {"currentLowASRAlarmCount", CallRouteQoS::LOW_ASR_COUNT},
1157 {"currentHighASRAlarmCount", CallRouteQoS::HIGH_ASR_COUNT},
1158 {"overallLowASRAlarmCount", CallRouteQoS::LOW_ASR_ALL_COUNT},
1159 {"overallHighASRAlarmCount", CallRouteQoS::HIGH_ASR_ALL_COUNT},
1160 {"currentLowNERAlarmCount", CallRouteQoS::LOW_NER_COUNT},
1161 {"overallLowNERAlarmCount", CallRouteQoS::LOW_NER_ALL_COUNT},
1162 {0,0}
1163 };
1164 // call end reasons
1165 static TokenDict s_endReasons[] = {
1166 {"User hangup", CallRouteQoS::HANGUP},
1167 {"Rejected", CallRouteQoS::REJECT},
1168 {"rejected", CallRouteQoS::REJECT},
1169 {"User busy", CallRouteQoS::BUSY},
1170 {"busy", CallRouteQoS::BUSY},
1171 {"Request Terminated", CallRouteQoS::NO_ANSWER},
1172 {"noanswer", CallRouteQoS::NO_ANSWER},
1173 {"No route to call target", CallRouteQoS::NO_ROUTE},
1174 {"noroute", CallRouteQoS::NO_ROUTE},
1175 {"Service Unavailable", CallRouteQoS::NO_CONN},
1176 {"noconn", CallRouteQoS::NO_CONN},
1177 {"service-unavailable", CallRouteQoS::NO_CONN},
1178 {"Unauthorized", CallRouteQoS::NO_AUTH},
1179 {"noauth", CallRouteQoS::NO_AUTH},
1180 {"Cancelled", CallRouteQoS::CANCELLED},
1181 {"Congestion", CallRouteQoS::CONGESTION},
1182 {"congestion", CallRouteQoS::CONGESTION},
1183 {"Unsupported Media Type", CallRouteQoS::NO_MEDIA},
1184 {"nomedia", CallRouteQoS::NO_MEDIA},
1185 {0,0}
1186 };
1187
1188 static TokenDict s_callCounterQueries[] = {
1189 {"incomingCalls", CallMonitor::INCOMING_CALLS},
1190 {"outgoingCalls", CallMonitor::OUTGOING_CALLS},
1191 {"qosDirectionsCount", CallMonitor::ROUTES_COUNT},
1192 {0,0}
1193 };
1194
1195 static TokenDict s_activeCallInfo[] = {
1196 {"activeCallsCount", ActiveCallsInfo::COUNT},
1197 {"callEntryID", ActiveCallsInfo::ID},
1198 {"callEntryIndex", ActiveCallsInfo::INDEX},
1199 {"callEntryID", ActiveCallsInfo::ID},
1200 {"callEntryStatus", ActiveCallsInfo::STATUS},
1201 {"callEntryCaller", ActiveCallsInfo::CALLER},
1202 {"callEntryCalled", ActiveCallsInfo::CALLED},
1203 {"callEntryPeerChan", ActiveCallsInfo::PEER},
1204 {"callEntryDuration", ActiveCallsInfo::DURATION},
1205 {0,0}
1206 };
1207
1208 TokenDict TrunkInfo::s_trunkInfo[] = {
1209 {"trunksCount", TrunkInfo::COUNT},
1210 {"trunkIndex", TrunkInfo::INDEX},
1211 {"trunkID", TrunkInfo::ID},
1212 {"trunkType", TrunkInfo::TYPE},
1213 {"trunkCircuitCount", TrunkInfo::CIRCUITS},
1214 {"trunkCurrentCallsCount", TrunkInfo::CALLS},
1215 {"trunkDownAlarms", TrunkInfo::ALARMS_COUNT},
1216 {"trunkCircuitsLocked", TrunkInfo::LOCKED},
1217 {"trunkCircuitsIdle", TrunkInfo::IDLE},
1218 {0,0}
1219 };
1220
1221 TokenDict TrunkInfo::s_trunkStatus[] = {
1222 {"module", TrunkInfo::SKIP},
1223 {"trunk", TrunkInfo::ID},
1224 {"type", TrunkInfo::TYPE},
1225 {"circuits", TrunkInfo::CIRCUITS},
1226 {"calls", TrunkInfo::CALLS},
1227 {"status", TrunkInfo::STATUS},
1228 {"locked", TrunkInfo::LOCKED},
1229 {"idle", TrunkInfo::IDLE},
1230 {0,0}
1231 };
1232
1233 static TokenDict s_accountInfo[] = {
1234 {"accountsCount", AccountsInfo::COUNT},
1235 {"accountIndex", AccountsInfo::INDEX},
1236 {"accountID", AccountsInfo::ID},
1237 {"accountStatus", AccountsInfo::STATUS},
1238 {"accountProtocol", AccountsInfo::PROTO},
1239 {"accountUsername", AccountsInfo::USERNAME},
1240 {0,0}
1241 };
1242
1243 static TokenDict s_engineQuery[] = {
1244 {"plugins", EngineInfo::ENGINE_PLUGINS},
1245 {"handlers", EngineInfo::ENGINE_HANDLERS},
1246 {"messages", EngineInfo::ENGINE_MESSAGES},
1247 {"msgMaxQueued", EngineInfo::ENGINE_MAXQUEUED},
1248 {"msgLastSecond", EngineInfo::ENGINE_MSGRATE},
1249 {"msgMaxPerSecond", EngineInfo::ENGINE_MAXMSGRATE},
1250 {"msgEnqueued", EngineInfo::ENGINE_MSGENQUEUED},
1251 {"msgDequeued", EngineInfo::ENGINE_MSGDEQUEUED},
1252 {"msgDispatched", EngineInfo::ENGINE_MSGDISPATCHED},
1253 {"threads", EngineInfo::ENGINE_THREADS},
1254 {"workers", EngineInfo::ENGINE_WORKERS},
1255 {"mutexes", EngineInfo::ENGINE_MUTEXES},
1256 {"locks", EngineInfo::ENGINE_LOCKS},
1257 {"semaphores", EngineInfo::ENGINE_SEMAPHORES},
1258 {"waitingSemaphores", EngineInfo::ENGINE_WAITING},
1259 {"acceptStatus", EngineInfo::ENGINE_CALL_ACCEPT},
1260 // node info
1261 {"runAttempt", EngineInfo::ENGINE_RUNATTEMPT},
1262 {"name", EngineInfo::ENGINE_NODENAME},
1263 {"state", EngineInfo::ENGINE_STATE},
1264 {"unexpectedRestart", EngineInfo::ENGINE_UNEX_RESTART},
1265 {0,0}
1266 };
1267
1268 TokenDict EngineInfo::s_engineInfo[] = {
1269 {"type", EngineInfo::ENGINE_TYPE},
1270 {"plugins", EngineInfo::ENGINE_PLUGINS},
1271 {"handlers", EngineInfo::ENGINE_HANDLERS},
1272 {"messages", EngineInfo::ENGINE_MESSAGES},
1273 {"maxqueue", EngineInfo::ENGINE_MAXQUEUED},
1274 {"messagerate", EngineInfo::ENGINE_MSGRATE},
1275 {"maxmsgrate", EngineInfo::ENGINE_MAXMSGRATE},
1276 {"enqueued", EngineInfo::ENGINE_MSGENQUEUED},
1277 {"dequeued", EngineInfo::ENGINE_MSGDEQUEUED},
1278 {"dispatched", EngineInfo::ENGINE_MSGDISPATCHED},
1279 {"threads", EngineInfo::ENGINE_THREADS},
1280 {"workers", EngineInfo::ENGINE_WORKERS},
1281 {"mutexes", EngineInfo::ENGINE_MUTEXES},
1282 {"locks", EngineInfo::ENGINE_LOCKS},
1283 {"semaphores", EngineInfo::ENGINE_SEMAPHORES},
1284 {"waiting", EngineInfo::ENGINE_WAITING},
1285 {"runattempt", EngineInfo::ENGINE_RUNATTEMPT},
1286 {"nodename", EngineInfo::ENGINE_NODENAME},
1287 {"acceptcalls", EngineInfo::ENGINE_CALL_ACCEPT},
1288 {"lastsignal", EngineInfo::ENGINE_UNEX_RESTART},
1289 {0,0}
1290 };
1291
1292 TokenDict ModuleInfo::s_moduleInfo[] = {
1293 {"name", ModuleInfo::MODULE_NAME},
1294 {"type", ModuleInfo::MODULE_TYPE},
1295 {"format", ModuleInfo::MODULE_FORMAT},
1296 {0,0}
1297 };
1298
1299 static TokenDict s_moduleQuery[] = {
1300 {"moduleCount", ModuleInfo::COUNT},
1301 {"moduleIndex", ModuleInfo::INDEX},
1302 {"moduleName", ModuleInfo::MODULE_NAME},
1303 {"moduleType", ModuleInfo::MODULE_TYPE},
1304 {"moduleExtra", ModuleInfo::MODULE_INFO},
1305 {0,0}
1306 };
1307
1308 TokenDict DatabaseInfo::s_databaseInfo[] = {
1309 {"conns", DatabaseInfo::Connections},
1310 {"failed", DatabaseInfo::FailedConns},
1311 {0,0}
1312 };
1313
1314 static TokenDict s_databaseQuery[] = {
1315 {"successfulConnections", DatabaseInfo::Connections},
1316 {"failedConnections", DatabaseInfo::FailedConns},
1317 {"databaseCount", DatabaseInfo::Accounts},
1318 {0,0}
1319 };
1320
1321 static TokenDict s_dbAccountQueries[] = {
1322 {"databaseIndex", DatabaseAccount::AccountIndex},
1323 {"databaseAccount", DatabaseAccount::AccountName},
1324 {"queriesCount", DatabaseAccount::QueriesCount},
1325 {"failedQueries", DatabaseAccount::FailedCount},
1326 {"errorQueries", DatabaseAccount::ErrorsCount},
1327 {"queryExecTime", DatabaseAccount::ExecTime},
1328 // alarms counters
1329 {"tooManyQueriesAlarms", DatabaseAccount::TooManyAlrmCount},
1330 {"tooManyFailedQueriesAlarms", DatabaseAccount::TooManyFailedAlrmCount},
1331 {"tooManyErrorQueriesAlarms", DatabaseAccount::TooManyErrorAlrmCount},
1332 {"queryExecTooLongAlarms", DatabaseAccount::ExecTooLongAlrmCount},
1333 {"noConnectionAlarms", DatabaseAccount::NoConnAlrmCount},
1334 // database thresholds
1335 {"queriesCountThreshold", DatabaseAccount::MaxQueries},
1336 {"failedQueriesThreshold", DatabaseAccount::MaxFailedQueries},
1337 {"errorQueriesThreshold", DatabaseAccount::MaxErrorQueries},
1338 {"queryExecTimeThreshold", DatabaseAccount::MaxExecTime},
1339 // alarm
1340 {"tooManyQueries", DatabaseAccount::TooManyAlrm},
1341 {"tooManyFailedQueries", DatabaseAccount::TooManyFailedAlrm},
1342 {"tooManyErrorQueries", DatabaseAccount::TooManyErrorAlrm},
1343 {"queryExecTimeTooLong", DatabaseAccount::ExecTooLongAlrm},
1344 {"noConnection", DatabaseAccount::NoConnAlrm},
1345 {0,0}
1346 };
1347
1348 static TokenDict s_dbAccountInfo[] = {
1349 {"maxqueries", DatabaseAccount::MaxQueries},
1350 {"maxfailed", DatabaseAccount::MaxFailedQueries},
1351 {"maxerrors", DatabaseAccount::MaxErrorQueries},
1352 {"maxtimeperquery", DatabaseAccount::MaxExecTime},
1353 {"total", DatabaseAccount::TOTAL_IDX},
1354 {"failed", DatabaseAccount::FAILED_IDX},
1355 {"errorred", DatabaseAccount::ERROR_IDX},
1356 {"querytime", DatabaseAccount::TIME_IDX},
1357 {"hasconn", DatabaseAccount::CONN_IDX},
1358 {0,0}
1359 };
1360
1361 TokenDict RTPEntry::s_rtpInfo[] = {
1362 {"remoteip", RTPEntry::Direction},
1363 {"noaudio", RTPEntry::NoAudio},
1364 {"lostaudio", RTPEntry::LostAudio},
1365 {"lostpkts", RTPEntry::PktsLost},
1366 {"synclost", RTPEntry::SyncLost},
1367 {"seqslost", RTPEntry::SeqLost},
1368 {"wrongsrc", RTPEntry::WrongSRC},
1369 {"wrongssrc", RTPEntry::WrongSSRC},
1370 {0,0}
1371 };
1372
1373 static TokenDict s_rtpQuery[] = {
1374 {"rtpDirectionsCount", RTPEntry::Count},
1375 {"rtpEntryIndex", RTPEntry::Index},
1376 {"rtpDirection", RTPEntry::Direction},
1377 {"noAudioCounter", RTPEntry::NoAudio},
1378 {"lostAudioCounter", RTPEntry::LostAudio},
1379 {"packetsLost", RTPEntry::PktsLost},
1380 {"syncLost", RTPEntry::SyncLost},
1381 {"sequenceNumberLost", RTPEntry::SeqLost},
1382 {"wrongSRC", RTPEntry::WrongSRC},
1383 {"wrongSSRC", RTPEntry::WrongSSRC},
1384 {0,0}
1385 };
1386
1387 static TokenDict s_sigTypes[] = {
1388 {"ss7-mtp3", Monitor::SS7_MTP3},
1389 {"trunk", Monitor::TRUNK},
1390 {"isdn-q921", Monitor::ISDN},
1391 {0,0}
1392 };
1393
1394 static TokenDict s_sipNotifs[] = {
1395 {"transactionsTimedOut", Monitor::TransactTimedOut},
1396 {"failedAuths", Monitor::FailedAuths},
1397 {"byesTimedOut", Monitor::ByesTimedOut},
1398 {"gatewayTimeout", Monitor::GWTimeout},
1399 {"gatewayUp", Monitor::GWUp},
1400 {0,0}
1401 };
1402
1403 static TokenDict s_mgcpNotifs[] = {
1404 {"mgcpTransactionsTimedOut", Monitor::TransactTimedOut},
1405 {"deleteTransactionsTimedOut", Monitor::DeletesTimedOut},
1406 {"mgcpGatewayTimedOut", Monitor::GWTimeout},
1407 {"mgcpGatewayUp", Monitor::GWUp},
1408 {0,0}
1409 };
1410
1411 static TokenDict s_sigNotifs[] = {
1412 {"trunkDown", Monitor::TrunkDown},
1413 {"trunkUp", Monitor::TrunkUp},
1414 {"linksetDown", Monitor::LinksetDown},
1415 {"linksetUp", Monitor::LinksetUp},
1416 {"linkUp", Monitor::LinkUp},
1417 {"linkDown", Monitor::LinkDown},
1418 {"linkUp", Monitor::LinkUp},
1419 {"isdnQ921Down", Monitor::IsdnQ921Down},
1420 {"isdnQ921Up", Monitor::IsdnQ921Up},
1421 {0,0}
1422 };
1423
1424 static TokenDict s_cardInfo[] = {
1425 {"interfaceDown", Monitor::InterfaceDown},
1426 {"interfaceUp", Monitor::InterfaceUp},
1427 {0,0}
1428 };
1429
1430 static TokenDict s_cardNotifs[] = {
1431 {"interfaceDown", Monitor::InterfaceDown},
1432 {"interfaceUp", Monitor::InterfaceUp},
1433 {0,0}
1434 };
1435
1436 TokenDict LinksetInfo::s_linksetInfo[] = {
1437 {"linksetCount", LinksetInfo::COUNT},
1438 {"linksetIndex", LinksetInfo::INDEX},
1439 {"linksetID", LinksetInfo::ID},
1440 {"linksetType", LinksetInfo::TYPE},
1441 {"linksetStatus", LinksetInfo::STATUS},
1442 {"linksetDownAlarms", LinksetInfo::ALARMS_COUNT},
1443 {0,0}
1444 };
1445
1446 TokenDict LinksetInfo::s_linksetStatus[] = {
1447 {"module", LinksetInfo::SKIP},
1448 {"component", LinksetInfo::ID},
1449 {"type", LinksetInfo::TYPE},
1450 {"status", LinksetInfo::STATUS},
1451 {0,0}
1452 };
1453
1454 TokenDict LinkInfo::s_linkInfo[] = {
1455 {"linkCount", LinkInfo::COUNT},
1456 {"linkIndex", LinkInfo::INDEX},
1457 {"linkID", LinkInfo::ID},
1458 {"linkType", LinkInfo::TYPE},
1459 {"linkStatus", LinkInfo::STATUS},
1460 {"linkDownAlarms", LinkInfo::ALARMS_COUNT},
1461 {"linkUptime", LinkInfo::UPTIME},
1462 {0,0}
1463 };
1464
1465 TokenDict InterfaceInfo::s_ifacesInfo[] = {
1466 {"interfacesCount", InterfaceInfo::COUNT},
1467 {"interfaceIndex", InterfaceInfo::INDEX},
1468 {"interfaceID", InterfaceInfo::ID},
1469 {"interfaceStatus", InterfaceInfo::STATUS},
1470 {"interfaceDownAlarms", InterfaceInfo::ALARMS_COUNT},
1471 {0,0}
1472 };
1473
1474 String DatabaseInfo::s_dbParam = "database.";
1475 String DatabaseInfo::s_totalParam = "total.";
1476 String DatabaseInfo::s_failedParam = "failed.";
1477 String DatabaseInfo::s_errorParam = "errorred.";
1478 String DatabaseInfo::s_hasConnParam = "hasconn.";
1479 String DatabaseInfo::s_timeParam = "querytime.";
1480
1481 INIT_PLUGIN(Monitor);
1482
UNLOAD_PLUGIN(unloadNow)1483 UNLOAD_PLUGIN(unloadNow)
1484 {
1485 if (unloadNow && !__plugin.unload())
1486 return false;
1487 return true;
1488 }
1489
1490
1491 /**
1492 * Class MsgUpdateHandler
1493 * Class for handling a "module.update" message
1494 */
1495 class MsgUpdateHandler : public MessageHandler
1496 {
1497 public:
MsgUpdateHandler(unsigned int priority=100)1498 inline MsgUpdateHandler(unsigned int priority = 100)
1499 : MessageHandler("module.update",priority,__plugin.name())
1500 { }
~MsgUpdateHandler()1501 virtual ~MsgUpdateHandler()
1502 { }
1503 virtual bool received(Message& msg);
1504 };
1505
1506 /**
1507 * Class SnmpMsgHandler
1508 * Class for handling a "monitor.query" message, message used for obtaining information from the monitor
1509 */
1510 class SnmpMsgHandler : public MessageHandler
1511 {
1512 public:
SnmpMsgHandler(unsigned int priority=100)1513 inline SnmpMsgHandler(unsigned int priority = 100)
1514 : MessageHandler("monitor.query",priority,__plugin.name())
1515 { }
~SnmpMsgHandler()1516 virtual ~SnmpMsgHandler()
1517 { }
1518 virtual bool received(Message& msg);
1519 };
1520
1521 /**
1522 * Class HangupHandler
1523 * Handler for "chan.hangup" message"
1524 */
1525 class HangupHandler : public MessageHandler
1526 {
1527 public:
HangupHandler(unsigned int priority=100)1528 inline HangupHandler(unsigned int priority = 100)
1529 : MessageHandler("chan.hangup",priority,__plugin.name())
1530 { }
~HangupHandler()1531 virtual ~HangupHandler()
1532 { }
1533 virtual bool received(Message& msg);
1534 };
1535
1536 /**
1537 * Class EngineStartHandler
1538 * Handler for "engine.start" message
1539 */
1540 class EngineStartHandler : public MessageHandler
1541 {
1542 public:
EngineStartHandler(unsigned int priority=100)1543 inline EngineStartHandler(unsigned int priority = 100)
1544 : MessageHandler("engine.start",priority,__plugin.name())
1545 { }
~EngineStartHandler()1546 virtual ~EngineStartHandler()
1547 { }
1548 virtual bool received(Message& msg);
1549 };
1550
1551 /**
1552 * Class AuthHandler
1553 * Handler for a "user.auth" message. It counts the number of authentication requests
1554 */
1555 class AuthHandler : public MessageHandler
1556 {
1557 public:
AuthHandler()1558 inline AuthHandler()
1559 : MessageHandler("user.auth",1,__plugin.name()),
1560 m_count(0)
1561 { }
~AuthHandler()1562 virtual ~AuthHandler()
1563 { }
1564 virtual bool received(Message& msg);
1565 // return the number of authentication requests
getCount()1566 inline unsigned int getCount()
1567 { return m_count; }
1568 private:
1569 unsigned int m_count;
1570 };
1571
1572 /**
1573 * Class RegisterHandler
1574 * Handler for a "user.register" message. It counts the number of register requests
1575 */
1576 class RegisterHandler : public MessageHandler
1577 {
1578 public:
RegisterHandler()1579 inline RegisterHandler()
1580 : MessageHandler("user.register",1,__plugin.name()),
1581 m_count(0)
1582 { }
~RegisterHandler()1583 virtual ~RegisterHandler()
1584 { }
1585 virtual bool received(Message& msg);
1586 // return the count
getCount()1587 inline unsigned int getCount()
1588 { return m_count; }
1589 private:
1590 unsigned int m_count;
1591 };
1592
1593
1594 // helper function to get rid of new line characters
cutNewLine(String & line)1595 static void cutNewLine(String& line) {
1596 if (line.endsWith("\n"))
1597 line = line.substr(0,line.length() - 1);
1598 if (line.endsWith("\r"))
1599 line = line.substr(0,line.length() - 1);
1600 }
1601
1602 // callback for engine alarm hook
alarmCallback(const char * message,int level,const char * component,const char * info)1603 static void alarmCallback(const char* message, int level, const char* component, const char* info)
1604 {
1605 if (TelEngine::null(component) || TelEngine::null(message))
1606 return;
1607 const char* lvl = debugLevelName(level);
1608 if (TelEngine::null(lvl))
1609 return;
1610 TempObjectCounter cnt(__plugin.objectsCounter());
1611 Message* msg = new Message("module.update");
1612 msg->addParam("module",__plugin.name());
1613 msg->addParam("level",String(level));
1614 msg->addParam("from",component,false);
1615 msg->addParam("text",message,false);
1616 msg->addParam("info",info,false);
1617 Engine::enqueue(msg);
1618 if ((s_alarmThreshold >= DebugFail) && (level <= s_alarmThreshold)) {
1619 msg = new Message("monitor.notify",0,true);
1620 msg->addParam("notify","genericAlarm");
1621 msg->addParam("notify.0","alarmSource");
1622 msg->addParam("value.0",component);
1623 msg->addParam("notify.1","alarmLevel");
1624 msg->addParam("value.1",lvl);
1625 msg->addParam("notify.2","alarmText");
1626 msg->addParam("value.2",message);
1627 if (!TelEngine::null(info)) {
1628 msg->addParam("notify.3","alarmInfo");
1629 msg->addParam("value.3",info);
1630 }
1631 Engine::enqueue(msg);
1632 }
1633 }
1634
1635
1636 /**
1637 * MsgUpdateHandler
1638 */
received(Message & msg)1639 bool MsgUpdateHandler::received(Message& msg)
1640 {
1641 DDebug(__plugin.name(),DebugAll,"MsgUpdateHandler::received()");
1642 __plugin.update(msg);
1643 return true;
1644 }
1645
1646 /**
1647 * SnmpMsgHandler
1648 */
received(Message & msg)1649 bool SnmpMsgHandler::received(Message& msg)
1650 {
1651 DDebug(__plugin.name(),DebugAll,"SnmpMsgHandler::received()");
1652 return __plugin.solveQuery(msg);
1653 }
1654
1655 /**
1656 * HangupHandler
1657 */
received(Message & msg)1658 bool HangupHandler::received(Message& msg)
1659 {
1660 DDebug(__plugin.name(),DebugAll,"HangupHandler::received()");
1661 String status = msg.getValue("status","");
1662 String address = msg.getValue("address","");
1663 int cause = msg.getIntValue("cause_sip",0);
1664 if (status == "outgoing" && cause && !address.null())
1665 __plugin.handleChanHangup(address,cause);
1666 if (status == "ringing" && !address.null())
1667 __plugin.verifyGateway(address);
1668 return false;
1669 }
1670
1671 /**
1672 * EngineStartHandler
1673 */
received(Message & msg)1674 bool EngineStartHandler::received(Message& msg)
1675 {
1676 DDebug(__plugin.name(),DebugAll,"EngineStartHandler::received()");
1677 s_yateRun = Engine::runParams().getIntValue("runattempt",0);
1678 if (s_yateRun >= s_yateRunAlarm && s_yateRunAlarm >= 1) {
1679 String notif = lookup(EngineInfo::ENGINE_RUNATTEMPT,s_engineQuery,"");
1680 if (!notif.null())
1681 __plugin.sendTrap(notif,String(s_yateRun));
1682 }
1683 int lastsignal = Engine::runParams().getIntValue(YSTRING("lastsignal"),0);
1684 if (lastsignal > 0) {
1685 String notif = lookup(EngineInfo::ENGINE_UNEX_RESTART,s_engineQuery,"");
1686 if (!notif.null())
1687 __plugin.sendTrap(notif,String(lastsignal));
1688 }
1689 return false;
1690 };
1691
1692 /**
1693 * AuthHandler
1694 */
received(Message & msg)1695 bool AuthHandler::received(Message& msg)
1696 {
1697 String user = msg.getValue("username","");
1698 if (!user.null())
1699 m_count++;
1700 return false;
1701 }
1702
1703 /**
1704 * RegisterHandler
1705 */
received(Message & msg)1706 bool RegisterHandler::received(Message& msg)
1707 {
1708 m_count++;
1709 return false;
1710 }
1711
1712 /**
1713 * Cache
1714 */
~Cache()1715 Cache::~Cache()
1716 {
1717 discard();
1718 }
1719
load()1720 bool Cache::load()
1721 {
1722 return false;
1723 }
1724
1725 // discard cached data
discard()1726 void Cache::discard()
1727 {
1728 DDebug(&__plugin,DebugInfo,"Cache::discard() [%p] - dropping cached data",this);
1729 Lock l(this);
1730 m_reload = true;
1731 m_table.clear();
1732 }
1733
getInfo(const String & query,unsigned int & index,TokenDict * dict)1734 String Cache::getInfo(const String& query, unsigned int& index, TokenDict* dict)
1735 {
1736 DDebug(&__plugin,DebugAll,"Cache::getInfo(query='%s',index='%d') [%p]",query.c_str(),index,this);
1737 // if we have data, check if it is still valid
1738 if (isExpired())
1739 discard();
1740 else
1741 // if the data has not yet expired, update the expire time
1742 updateExpire();
1743
1744 String retStr;
1745 // if the is no data available, obtain it from an engine.status message
1746 if (m_reload && !load())
1747 return retStr;
1748
1749 Lock l(this);
1750 // lookup the type of the query, and if it's of type COUNT, return the number of entries
1751 int type = lookup(query,dict,0);
1752 if (type == COUNT) {
1753 retStr += m_table.count();
1754 return retStr;
1755 }
1756 // if it's not of type COUNT, check if the requested index is within the range of the table
1757 if (index < 1 || index > m_table.count())
1758 return retStr;
1759 // get the entry of the given index
1760 NamedList* nl = static_cast<NamedList*>(m_table[index - 1]);
1761 if (!nl)
1762 return retStr;
1763 // get the result
1764 if (type == INDEX) {
1765 retStr += index;
1766 return retStr;
1767 }
1768 retStr = nl->getValue(query,"");
1769 if (retStr.null())
1770 retStr = "no info";
1771 return retStr;
1772 }
1773
1774 /**
1775 * ActiveCallInfo
1776 */
1777 // match bill ids to find a call's peer
checkPeers(const String & billID,const String & id)1778 String ActiveCallsInfo::checkPeers(const String& billID, const String& id)
1779 {
1780 DDebug(&__plugin,DebugAll,"ActiveCallsInfo::checkPeers('%s','%s')",billID.c_str(),id.c_str());
1781 if (billID.null())
1782 return id;
1783 String retPeers;
1784 for(ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
1785 NamedList* nl = static_cast<NamedList*>(o->get());
1786 if (!nl)
1787 continue;
1788 String otherBillID = nl->getValue("billId","");
1789 String peers = nl->getValue(lookup(PEER,s_activeCallInfo,0),"");
1790 if (billID == otherBillID) {
1791 String otherID = nl->getValue(lookup(ID,s_activeCallInfo,0),"");
1792 peers.append(id,";");
1793 retPeers.append(otherID,";");
1794 }
1795 nl->setParam(lookup(PEER,s_activeCallInfo,0),peers);
1796 }
1797 return retPeers;
1798 }
1799
1800 // load data into the cache
load()1801 bool ActiveCallsInfo::load()
1802 {
1803 DDebug(&__plugin,DebugInfo,"ActiveCallsInfo::load() [%p] - loading data",this);
1804 // emit an engine.status message
1805 Message m("engine.status");
1806 m.addParam("module","cdrbuild");
1807 Engine::dispatch(m);
1808 String& status = m.retValue();
1809 if (TelEngine::null(status))
1810 return false;
1811
1812 Lock l(this);
1813 m_table.clear();
1814
1815 int pos = status.rfind(';');
1816 if (pos < 0)
1817 return false;
1818
1819 status = status.substr(pos + 1);
1820 ObjList* calls = status.split(',');
1821 for (ObjList* o = calls->skipNull(); o; o = o->skipNext()) {
1822 String* callInfo = static_cast<String*>(o->get());
1823 if ( *callInfo == "\r\n")
1824 continue;
1825 if (pos > -1) {
1826 int pos = callInfo->find("=");
1827 NamedList* nl = new NamedList("");
1828
1829 String id = callInfo->substr(0,pos);
1830 callInfo->startSkip(String(id + "="));
1831 nl->setParam(lookup(ID,s_activeCallInfo,0),id);
1832 int i = 0;
1833 String peers;
1834 while (i < 5) {
1835 pos = callInfo->find("|");
1836 if (pos < -1)
1837 break;
1838 String val = callInfo->substr(0,pos);
1839 callInfo->startSkip(String(val + "|"),false);
1840 val = val.uriUnescape();
1841 int p = -1;
1842 switch (i) {
1843 case 0:
1844 p = val.find("=");
1845 if (p > -1)
1846 val = val.substr(p+1);
1847 nl->setParam(lookup(STATUS,s_activeCallInfo,0),val);
1848 break;
1849 case 1:
1850 val = ( val.null() ? "no info" : val);
1851 nl->setParam(lookup(CALLER,s_activeCallInfo,0),val);
1852 break;
1853 case 2:
1854 val = ( val.null() ? "no info" : val);
1855 nl->setParam(lookup(CALLED,s_activeCallInfo,0),val);
1856 break;
1857 case 3:
1858 peers = checkPeers(val,nl->getValue(lookup(ID,s_activeCallInfo,0),""));
1859 nl->setParam("billId",val);
1860 nl->setParam(lookup(PEER,s_activeCallInfo,0),peers);
1861 break;
1862 case 4:
1863 cutNewLine(val);
1864 val = ( val.null() ? "no info" : val);
1865 nl->setParam(lookup(DURATION,s_activeCallInfo,0),val);
1866 break;
1867 default:
1868 break;
1869 }
1870 i++;
1871 }
1872 m_table.append(nl);
1873 }
1874 }
1875 TelEngine::destruct(calls);
1876 updateExpire();
1877 return true;
1878 }
1879
1880 /**
1881 * InterfaceInfo
1882 */
1883 // reimplementation of the Cache::discard() function, only resets the status information
discard()1884 void SigInfo::discard()
1885 {
1886 if (!m_dictionary)
1887 return;
1888 DDebug(&__plugin,DebugAll,"SigInfo::discard() [%p] - dropping cached data",this);
1889 for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
1890 NamedList* nl = static_cast<NamedList*>(o->get());
1891 nl->setParam(lookup(STATUS,m_dictionary,""),"unknown");
1892 }
1893 m_reload = true;
1894 }
1895
1896 // STUB
load()1897 bool SigInfo::load()
1898 {
1899 DDebug(&__plugin,DebugWarn,"SigInfo::load() [%p] - STUB",this);
1900 return true;
1901 }
1902
1903 // increase the counter for number of alarms sent
updateAlarmCounter(const String & name)1904 void SigInfo::updateAlarmCounter(const String& name)
1905 {
1906 if (!m_dictionary)
1907 return;
1908 DDebug(&__plugin,DebugAll,"SigInfo::updateAlarmCounter('%s') [%p]",name.c_str(),this);
1909 NamedList* nl = static_cast<NamedList*>(m_table[name]);
1910 if (!nl) {
1911 load();
1912 nl = static_cast<NamedList*>(m_table[name]);
1913 }
1914 if (nl) {
1915 String param = lookup(ALARMS_COUNT,m_dictionary,"");
1916 if (param.null())
1917 return;
1918 int val = nl->getIntValue(param,0);
1919 val++;
1920 nl->setParam(param,String(val));
1921 }
1922 }
1923
1924 /**
1925 * InterfaceInfo
1926 */
1927 // parse interface information and store it in the cache
load()1928 bool InterfaceInfo::load()
1929 {
1930 DDebug(&__plugin,DebugAll,"InterfaceInfo::load() [%p] - updating internal data",this);
1931 Message m("engine.status");
1932 m.addParam("module","sig ifaces");
1933 Engine::dispatch(m);
1934 String& status = m.retValue();
1935 if (!TelEngine::null(status)) {
1936 cutNewLine(status);
1937 ObjList* parts = status.split(';');
1938 if (!(parts && parts->count() > 2)) {
1939 TelEngine::destruct(parts);
1940 return true;
1941 }
1942 String ifaces = static_cast<String*>(parts->at(2));
1943 if (ifaces.null()) {
1944 TelEngine::destruct(parts);
1945 return true;
1946 }
1947 Lock l(this);
1948 ObjList* list = ifaces.split(',');
1949 for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
1950 String iface = static_cast<String*>(o->get());
1951 String name, status;
1952 iface.extractTo("=",name).extractTo("|",status);
1953 if (name.null())
1954 continue;
1955 NamedList* nl = static_cast<NamedList*>(m_table[name]);
1956 if (!nl) {
1957 nl = new NamedList(name);
1958 nl->setParam(lookup(ID,s_ifacesInfo,""),name);
1959 nl->setParam(lookup(STATUS,s_ifacesInfo,""),status);
1960 m_table.append(nl);
1961 }
1962 else
1963 nl->setParam(lookup(STATUS,s_ifacesInfo,""),status);
1964 if (!nl->getParam(lookup(ALARMS_COUNT,s_ifacesInfo,"")))
1965 nl->setParam(lookup(ALARMS_COUNT,s_ifacesInfo,""),"0");
1966 }
1967 TelEngine::destruct(list);
1968 TelEngine::destruct(parts);
1969 }
1970 updateExpire();
1971 return true;
1972 }
1973
1974 /**
1975 * LinkInfo
1976 */
1977 // parse link information
load()1978 bool LinkInfo::load()
1979 {
1980 DDebug(&__plugin,DebugAll,"LinkInfo::load() [%p] - loading data",this);
1981 Message m("engine.status");
1982 m.addParam("module","sig links");
1983 Engine::dispatch(m);
1984 String& status = m.retValue();
1985 if (!TelEngine::null(status)) {
1986 cutNewLine(status);
1987 ObjList* parts = status.split(';');
1988 if (!(parts && parts->count() > 2)) {
1989 TelEngine::destruct(parts);
1990 return true;
1991 }
1992 String links = static_cast<String*>(parts->at(2));
1993 if (links.null()) {
1994 TelEngine::destruct(parts);
1995 return true;
1996 }
1997 Lock l(this);
1998 ObjList* list = links.split(',');
1999 for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
2000 String link = static_cast<String*>(o->get());
2001 String name,type,status;
2002 int uptime = 0;
2003 link.extractTo("=",name).extractTo("|",type).
2004 extractTo("|",status).extractTo("|",uptime);
2005 if (name.null() || type.null())
2006 continue;
2007 NamedList* nl = static_cast<NamedList*>(m_table[name]);
2008 if (!nl) {
2009 nl = new NamedList(name);
2010 nl->setParam(lookup(ID,s_linkInfo,""),name);
2011 nl->setParam(lookup(TYPE,s_linkInfo,""),type);
2012 nl->setParam(lookup(STATUS,s_linkInfo,""),status);
2013 nl->setParam(lookup(UPTIME,s_linkInfo,""),String(uptime));
2014 m_table.append(nl);
2015 }
2016 else {
2017 nl->setParam(lookup(STATUS,s_linkInfo,""),status);
2018 nl->setParam(lookup(UPTIME,s_linkInfo,""),String(uptime));
2019 }
2020 if (!nl->getParam(lookup(ALARMS_COUNT,s_linkInfo,"")))
2021 nl->setParam(lookup(ALARMS_COUNT,s_linkInfo,""),"0");
2022 if (!nl->getParam(lookup(UPTIME,s_linkInfo,"")))
2023 nl->setParam(lookup(UPTIME,s_linkInfo,""),"0");
2024 }
2025 TelEngine::destruct(list);
2026 TelEngine::destruct(parts);
2027 }
2028 updateExpire();
2029 return true;
2030 }
2031
2032 /**
2033 * LinksetInfo
2034 */
2035 // parse linkset information
load()2036 bool LinksetInfo::load()
2037 {
2038 DDebug(&__plugin,DebugAll,"LinksetInfo::load() [%p] - loading data",this);
2039 Message m("engine.command");
2040 m.addParam("partial","status sig ");
2041 m.addParam("partline","status sig");
2042 if (!Engine::dispatch(m))
2043 return false;
2044 String& status = m.retValue();
2045 if (TelEngine::null(status))
2046 return false;
2047
2048 Lock l(this);
2049 ObjList* links = status.split('\t');
2050 for (ObjList* o = links->skipNull(); o; o = o->skipNext()) {
2051 String* link = static_cast<String*>(o->get());
2052 if (*link == "links" || *link == "ifaces")
2053 continue;
2054 Message msg("engine.status");
2055 msg.addParam("module",String("sig " + *link));
2056 Engine::dispatch(msg);
2057 String& linkInfo = msg.retValue();
2058 if (linkInfo.null())
2059 continue;
2060 NamedList* nl = static_cast<NamedList*>(m_table[*link]);
2061 if (nl)
2062 parseLinksetInfo(linkInfo,*link,nl);
2063 else {
2064 nl = parseLinksetInfo(linkInfo,*link);
2065 if (nl)
2066 m_table.append(nl);
2067 }
2068 }
2069 TelEngine::destruct(links);
2070 updateExpire();
2071 return true;
2072 }
2073
2074 // parse the information about a single linkset
parseLinksetInfo(String & info,const String & link,NamedList * infoFill)2075 NamedList* LinksetInfo::parseLinksetInfo(String& info,const String& link, NamedList* infoFill)
2076 {
2077 cutNewLine(info);
2078 DDebug(&__plugin,DebugAll,"LinksetInfo::parseLinkInfo(info='%s',link='%s', infoFill='%p') [%p]",info.c_str(),link.c_str(),infoFill,this);
2079 NamedList* nl = (infoFill ? infoFill : new NamedList(link));
2080
2081 ObjList* parts = info.split(';',false);
2082 for (ObjList* obj = parts->skipNull(); obj; obj = obj->skipNext()) {
2083 String* infoPart = static_cast<String*>(obj->get());
2084 if (TelEngine::null(infoPart))
2085 continue;
2086 ObjList* params = infoPart->split(',',false);
2087 for (ObjList* o = params->skipNull(); o; o = o->skipNext()) {
2088 String* param = static_cast<String*>(o->get());
2089 int pos = param->find("=");
2090 if (pos < 0)
2091 continue;
2092 String nameParam = param->substr(0,pos);
2093 String valParam = param->substr(pos + 1);
2094 int type = lookup(nameParam,s_linksetStatus,0);
2095 if (type > 0) {
2096 if (type == TYPE && (valParam.null() || valParam != "ss7-mtp3")) {
2097 TelEngine::destruct(params);
2098 TelEngine::destruct(nl);
2099 TelEngine::destruct(parts);
2100 return 0;
2101 }
2102 nl->setParam(lookup(type,s_linksetInfo,""),valParam);
2103 }
2104 }
2105 TelEngine::destruct(params);
2106 }
2107 NamedString* linksetId = nl->getParam(lookup(ID,s_linksetInfo));
2108 NamedString* typeStr = nl->getParam(lookup(TYPE,s_linksetInfo));
2109 if (TelEngine::null(linksetId) || TelEngine::null(typeStr)) {
2110 TelEngine::destruct(parts);
2111 TelEngine::destruct(nl);
2112 return 0;
2113 }
2114 TelEngine::destruct(parts);
2115 if (!nl->getParam(lookup(ALARMS_COUNT,s_linksetInfo,"")))
2116 nl->setParam(lookup(ALARMS_COUNT,s_linksetInfo,""),"0");
2117 return nl;
2118 }
2119
2120 /**
2121 * TrunkInfo
2122 */
2123 // reset trunk information
discard()2124 void TrunkInfo::discard()
2125 {
2126 DDebug(&__plugin,DebugAll,"TrunkInfo::discard() [%p] - dropping cached data",this);
2127 for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
2128 NamedList* nl = static_cast<NamedList*>(o->get());
2129 nl->setParam(lookup(TYPE,s_trunkInfo,""),"");
2130 nl->setParam(lookup(CIRCUITS,s_trunkInfo,""),"0");
2131 nl->setParam(lookup(CALLS,s_trunkInfo,""),"0");
2132 nl->setParam(lookup(LOCKED,s_trunkInfo,""),"0");
2133 nl->setParam(lookup(IDLE,s_trunkInfo,""),"0");
2134 }
2135 m_reload = true;
2136 }
2137
2138 // parse and load trunk information
load()2139 bool TrunkInfo::load()
2140 {
2141 DDebug(&__plugin,DebugAll,"TrunkInfo::load() [%p] - loading data",this);
2142 Message m("engine.command");
2143 m.addParam("partial","status sig ");
2144 m.addParam("partline","status sig");
2145 if (!Engine::dispatch(m))
2146 return false;
2147 String& status = m.retValue();
2148 if (TelEngine::null(status))
2149 return false;
2150
2151 Lock l(this);
2152 ObjList* trunks = status.split('\t');
2153 for (ObjList* o = trunks->skipNull(); o; o = o->skipNext()) {
2154 String* trunk = static_cast<String*>(o->get());
2155 if ((*trunk) == "links" || (*trunk) == "ifaces")
2156 continue;
2157 Message msg("engine.status");
2158 msg.addParam("module",String("sig " + *trunk));
2159 Engine::dispatch(msg);
2160 String& trunkInfo = msg.retValue();
2161 if (trunkInfo.null())
2162 continue;
2163 NamedList* nl = static_cast<NamedList*>(m_table[*trunk]);
2164 if (nl)
2165 parseTrunkInfo(trunkInfo,*trunk,nl);
2166 else {
2167 nl = parseTrunkInfo(trunkInfo,*trunk);
2168 if (nl)
2169 m_table.append(nl);
2170 }
2171 }
2172 TelEngine::destruct(trunks);
2173 // update the expire time
2174 updateExpire();
2175 return true;
2176 }
2177
2178 // parse the information for a single trunk
parseTrunkInfo(String & info,const String & trunk,NamedList * infoFill)2179 NamedList* TrunkInfo::parseTrunkInfo(String& info, const String& trunk, NamedList* infoFill)
2180 {
2181 cutNewLine(info);
2182 DDebug(&__plugin,DebugAll,"TrunkInfo::parseTrunkInfo(info='%s',trunk='%s', infoFill='%p') [%p]",info.c_str(),trunk.c_str(),infoFill,this);
2183 NamedList* nl = (infoFill ? infoFill : new NamedList(trunk));
2184
2185 ObjList* parts = info.split(';',false);
2186 for (ObjList* obj = parts->skipNull(); obj; obj = obj->skipNext()) {
2187 String* infoPart = static_cast<String*>(obj->get());
2188 if (TelEngine::null(infoPart))
2189 continue;
2190 ObjList* params = infoPart->split(',',false);
2191 for (ObjList* o = params->skipNull(); o; o = o->skipNext()) {
2192 String* param = static_cast<String*>(o->get());
2193 int pos = param->find("=");
2194 if (pos < 0) {
2195 TelEngine::destruct(params);
2196 continue;
2197 }
2198 String nameParam = param->substr(0,pos);
2199 String valParam = param->substr(pos + 1);
2200
2201 int type = lookup(nameParam,s_trunkStatus,0);
2202 if (type > 0)
2203 nl->setParam(lookup(type,s_trunkInfo,""),valParam);
2204 }
2205 TelEngine::destruct(params);
2206 }
2207 // check that it's indeed a trunk
2208 NamedString* trunkId = nl->getParam(lookup(ID,s_trunkInfo));
2209 if (TelEngine::null(trunkId)) {
2210 TelEngine::destruct(parts);
2211 TelEngine::destruct(nl);
2212 return 0;
2213 }
2214 TelEngine::destruct(parts);
2215 if (!nl->getParam(lookup(ALARMS_COUNT,s_trunkInfo,"")))
2216 nl->setParam(lookup(ALARMS_COUNT,s_trunkInfo,""),"0");
2217 return nl;
2218 }
2219
2220 /**
2221 * AccountsInfo
2222 */
2223 // parse and store accounts information
load()2224 bool AccountsInfo::load()
2225 {
2226 DDebug(&__plugin,DebugAll,"AccountsInfo::load() [%p] - loading data",this);
2227 String modules[] = {"sip","h323","iax","jabberclient"};
2228
2229 int i = 0;
2230 while (i < 4) {
2231 Message m("engine.status");
2232 m.setParam("module",modules[i] + " accounts");
2233 Engine::dispatch(m);
2234 String& status = m.retValue();
2235 i++;
2236 if (TelEngine::null(status))
2237 continue;
2238
2239 cutNewLine(status);
2240 //find protocol
2241 String protoParam = "protocol=";
2242 int pos = status.find(protoParam);
2243 if (pos < 0)
2244 continue;
2245 int auxPos = status.find(",",pos);
2246 if (auxPos < pos + (int)protoParam.length())
2247 continue;
2248 String proto = status.substr(pos + protoParam.length(),auxPos - (pos + protoParam.length()));
2249
2250 pos = status.rfind(';');
2251 if (pos < 0)
2252 continue;
2253 status = status.substr(pos + 1);
2254 Lock l(this);
2255 ObjList* accounts = status.split(',',false);
2256 for (ObjList* o = accounts->skipNull(); o; o = o->skipNext()) {
2257 String* account = static_cast<String*>(o->get());
2258 int pos1 = account->find("=");
2259 int pos2 = account->find("|");
2260 if (pos1 < 0 || pos2 < 0)
2261 continue;
2262 String name = account->substr(0,pos1).uriUnescape();
2263 String username = account->substr(pos1 + 1,pos2 - pos1 -1).uriUnescape();
2264 String status = account->substr(pos2 + 1);
2265
2266 if (name.null())
2267 continue;
2268 NamedList* nl = new NamedList("");
2269 nl->setParam(lookup(ID,s_accountInfo,""),name);
2270 nl->setParam(lookup(USERNAME,s_accountInfo,""),username);
2271 nl->setParam(lookup(STATUS,s_accountInfo,""),status);
2272 nl->setParam(lookup(PROTO,s_accountInfo,""),proto);
2273 m_table.append(nl);
2274 }
2275
2276 TelEngine::destruct(accounts);
2277 l.drop();
2278 }
2279 updateExpire();
2280 return true;
2281 }
2282
2283 /**
2284 * EngineInfo - engine status information cache
2285 */
2286 // reimplemented from Cache; obtain the result for a engine query
getInfo(const String query,unsigned int index,TokenDict * dict)2287 String EngineInfo::getInfo(const String query, unsigned int index, TokenDict* dict)
2288 {
2289 DDebug(&__plugin,DebugAll,"EngineInfo::getInfo(%s %d) [%p]",query.c_str(),index,this);
2290
2291 // if we have data, check if it is still valid
2292 if (isExpired())
2293 discard();
2294 else
2295 // if the data has not yet expired, update the expire time
2296 updateExpire();
2297
2298 String retStr;
2299 // if the is no data available, obtain it from an engine.status message
2300 if (m_reload && !load())
2301 return retStr;
2302
2303 Lock l(this);
2304 int type = lookup(query,s_engineQuery,0);
2305 if (!type)
2306 return retStr;
2307
2308 if (index > 1)
2309 return retStr;
2310 NamedList* nl = static_cast<NamedList*>(m_table[index]);
2311 if (!nl)
2312 return retStr;
2313 if (type == ENGINE_STATE)
2314 return s_nodeState;
2315 retStr = nl->getValue(query,"");
2316 if (retStr.null())
2317 retStr = "no info";
2318 return retStr;
2319 }
2320
2321 // load data into the cache
load()2322 bool EngineInfo::load()
2323 {
2324 DDebug(&__plugin,DebugInfo,"EngineInfo::load() [%p] - loading data",this);
2325 // emit an engine.status message
2326 Message m("engine.status");
2327 m.setParam("module","engine");
2328 Engine::dispatch(m);
2329 String& status = m.retValue();
2330 if (TelEngine::null(status))
2331 return false;
2332 cutNewLine(status);
2333
2334 Lock l(this);
2335 m_table.clear();
2336 NamedList* nl = new NamedList("");
2337 ObjList* params = status.split(';');
2338 for(ObjList* o = params->skipNull(); o; o = o->skipNext()) {
2339 String* strVal = static_cast<String*>(o->get());
2340 ObjList* l = strVal->split(',');
2341 for (ObjList* j = l->skipNull(); j; j = j->skipNext()) {
2342 String* str = static_cast<String*>(j->get());
2343 int pos = str->find("=");
2344 if (pos < 0)
2345 continue;
2346 String param = str->substr(0,pos);
2347 String value = str->substr(pos + 1);
2348 int type = lookup(param,s_engineInfo,0);
2349 if (!type)
2350 continue;
2351 nl->setParam(lookup(type,s_engineQuery,""),value);
2352 }
2353 TelEngine::destruct(l);
2354 }
2355 m_table.append(nl);
2356 TelEngine::destruct(params);
2357 updateExpire();
2358 return true;
2359 }
2360
2361 /**
2362 * ModuleInfo
2363 */
2364 // load data into the cache
load()2365 bool ModuleInfo::load()
2366 {
2367 DDebug(&__plugin,DebugInfo,"ModuleInfo::load() [%p] - loading data",this);
2368 // emit an engine.status message
2369 Message m("engine.status");
2370 m.setParam("details",String::boolText(false));
2371 Engine::dispatch(m);
2372 String& status = m.retValue();
2373 if (TelEngine::null(status))
2374 return false;
2375
2376 Lock l(this);
2377 m_table.clear();
2378
2379 // split the info into lines
2380 ObjList* lines = status.split('\n',false);
2381 for (ObjList* o = lines->skipNull(); o; o = o->skipNext()) {
2382 String* line = static_cast<String*>(o->get());
2383 if (!line)
2384 continue;
2385 cutNewLine(*line);
2386 ObjList* parts = line->split(';');
2387 NamedList* nl = new NamedList("");
2388 for (ObjList* p = parts->skipNull(); p; p = p->skipNext()) {
2389 String* str = static_cast<String*>(p->get());
2390 ObjList* paramVal = str->split(',');
2391 String info = "";
2392 for (ObjList* l = paramVal->skipNull(); l; l = l->skipNext()) {
2393 String* pair = static_cast<String*>(l->get());
2394 int pos = pair->find("=");
2395 if (pos < 0)
2396 continue;
2397 String param = pair->substr(0,pos);
2398 String value = pair->substr(pos + 1);
2399 int type = lookup(param,s_moduleInfo,0);
2400 if (!type) {
2401 info += (info.null() ? pair : String("," + *pair));
2402 continue;
2403 }
2404 nl->setParam(lookup(type,s_moduleQuery,""),value);
2405 }
2406 nl->setParam(lookup(MODULE_INFO,s_moduleQuery,""),info.uriUnescape());
2407 TelEngine::destruct(paramVal);
2408 }
2409 TelEngine::destruct(parts);
2410 if ( String("engine") == nl->getValue(lookup(MODULE_NAME,s_moduleQuery,""),"")) {
2411 TelEngine::destruct(nl);
2412 continue;
2413 }
2414 m_table.append(nl);
2415
2416 }
2417 TelEngine::destruct(lines);
2418 updateExpire();
2419 return true;
2420 }
2421
2422 /**
2423 * DatabaseAccount - an object holding information about a single monitored database account
2424 */
2425 // configure a database account for monitoring and initialize internal data
DatabaseAccount(const NamedList * cfg)2426 DatabaseAccount::DatabaseAccount(const NamedList* cfg)
2427 : m_resetTime(0), m_resetInterval(3600),
2428 m_isCurrent(true)
2429 {
2430 if (cfg) {
2431 Debug(&__plugin,DebugAll,"DatabaseAccount('%s') created for monitoring [%p]",cfg->c_str(),this);
2432 m_name = cfg->c_str();
2433 m_alarms = 0;
2434 for (int i = 0; i < ExecTime; i++) {
2435 m_dbCounters[i] = 0;
2436 m_prevDbCounters[i] = 0;
2437 }
2438 for (int i = 0; i <= NoConnAlrmCount - TooManyAlrmCount; i++)
2439 m_alarmCounters[i] = 0;
2440 updateConfig(cfg);
2441 m_resetTime = Time::secNow() + m_resetInterval;
2442 }
2443 m_isCurrent = true;
2444 }
2445
2446 // update internal data from a message received from the sql modules
update(const NamedList & info)2447 void DatabaseAccount::update(const NamedList& info)
2448 {
2449 XDebug(&__plugin,DebugAll,"DatabaseAccount::update() [%p]",this);
2450 for (unsigned int i = 0; i < info.count(); i++) {
2451 NamedString* ns = info.getParam(i);
2452 if (!(ns && *ns))
2453 continue;
2454 int type = lookup(ns->name(),s_dbAccountInfo,-1);
2455 if (type < 0)
2456 continue;
2457
2458 u_int16_t alarm = TOTAL_ALARM << (type);
2459 if (type <= TIME_IDX) {
2460 m_dbCounters[type] = ns->toInteger();
2461 if ((type != TIME_IDX) && (m_dbCounters[type] - m_prevDbCounters[type] >= m_thresholds[type]) && ((m_alarms & alarm) == 0)) {
2462 m_alarms |= alarm;
2463 m_alarmCounters[type]++;
2464 __plugin.sendTrap(lookup(TooManyAlrm + type,s_dbAccountQueries,""),toString(),index());
2465 }
2466 }
2467 if (type == CONN_IDX) {
2468 if (!ns->toBoolean()) {
2469 if ((m_alarms & alarm) == 0) {
2470 m_alarmCounters[CONN_IDX]++;
2471 m_alarms |= alarm;
2472 __plugin.sendTrap(lookup(NoConnAlrm,s_dbAccountQueries,""),toString(),index());
2473 }
2474 }
2475 else
2476 m_alarms &= ~alarm;
2477 }
2478 }
2479 // wait to gather all necessary data to compute average time
2480 double execTime = m_dbCounters[TIME_IDX] - m_prevDbCounters[TIME_IDX];
2481 double queriesNo = (m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]) - (m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]);
2482 if (queriesNo > 0 && (execTime / queriesNo / 1000) >= m_thresholds[TIME_IDX]) {
2483 if ((m_alarms & EXEC_ALARM) == 0) {
2484 m_alarms |= EXEC_ALARM;
2485 m_alarmCounters[TIME_IDX]++;
2486 __plugin.sendTrap(lookup(ExecTooLongAlrm,s_dbAccountQueries,""),toString(),index());
2487 }
2488 }
2489 else
2490 m_alarms &= ~EXEC_ALARM;
2491 }
2492
updateConfig(const NamedList * cfg)2493 void DatabaseAccount::updateConfig(const NamedList* cfg)
2494 {
2495 if (!cfg)
2496 return;
2497 for (int i = 0; i <= MaxExecTime - MaxQueries; i++)
2498 m_thresholds[i] = cfg->getIntValue(lookup(MaxQueries + i,s_dbAccountInfo,""),0);
2499 m_resetInterval = cfg->getIntValue("notiftime",3600);
2500 if (m_resetTime > Time::secNow() + m_resetInterval)
2501 m_resetTime = Time::secNow() + m_resetInterval;
2502 m_isCurrent = true;
2503 }
2504
2505 // obtain information from this entry
getInfo(unsigned int query)2506 const String DatabaseAccount::getInfo(unsigned int query)
2507 {
2508 DDebug(&__plugin,DebugAll,"DatabaseAccount::getInfo('%s') [%p]",lookup(query,s_dbAccountQueries,""),this);
2509 String ret = "";
2510 double execTime = 0;
2511 double queriesNo = 0;
2512 switch (query) {
2513 case QueriesCount:
2514 case FailedCount:
2515 case ErrorsCount:
2516 ret << m_dbCounters[query - 1] - m_prevDbCounters[query - 1];
2517 break;
2518 case ExecTime:
2519 execTime = m_dbCounters[TIME_IDX] - m_prevDbCounters[TIME_IDX];
2520 queriesNo = (m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]) - (m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]);
2521 if (queriesNo > 0)
2522 ret << (unsigned int) (execTime / queriesNo / 1000);
2523 else
2524 ret << 0;
2525 break;
2526 case TooManyAlrmCount:
2527 case TooManyFailedAlrmCount:
2528 case TooManyErrorAlrmCount:
2529 case ExecTooLongAlrmCount:
2530 case NoConnAlrmCount:
2531 ret << m_alarmCounters[query - TooManyAlrmCount];
2532 break;
2533 case MaxQueries:
2534 case MaxFailedQueries:
2535 case MaxErrorQueries:
2536 case MaxExecTime:
2537 ret << m_thresholds[query - MaxQueries];
2538 break;
2539 case AccountName:
2540 ret = m_name;
2541 break;
2542 case AccountIndex:
2543 ret = index();
2544 break;
2545 default:
2546 break;
2547 }
2548 return ret;
2549 }
2550
reset()2551 void DatabaseAccount::reset()
2552 {
2553 if (Time::secNow() < m_resetTime)
2554 return;
2555
2556 __plugin.sendTrap(lookup(QueriesCount,s_dbAccountQueries,""),String(m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]),index());
2557 __plugin.sendTrap(lookup(FailedCount,s_dbAccountQueries,""),String(m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]),index());
2558 __plugin.sendTrap(lookup(ErrorsCount,s_dbAccountQueries,""),String(m_dbCounters[ERROR_IDX] - m_prevDbCounters[ERROR_IDX]),index());
2559
2560 double execTime = m_dbCounters[TIME_IDX] - m_prevDbCounters[TIME_IDX];
2561 double queriesNo = (m_dbCounters[TOTAL_IDX] - m_prevDbCounters[TOTAL_IDX]) - (m_dbCounters[FAILED_IDX] - m_prevDbCounters[FAILED_IDX]);
2562 unsigned int time = 0;
2563 if (queriesNo > 0)
2564 time = (unsigned int) (execTime / queriesNo / 1000);
2565 __plugin.sendTrap(lookup(ExecTime,s_dbAccountQueries,""),String(time),index());
2566
2567 m_alarms = 0;
2568 for (unsigned int i = 0; i < CONN_IDX; i++)
2569 m_prevDbCounters[i] = m_dbCounters[i];
2570
2571 m_resetTime = Time::secNow() + m_resetInterval;
2572 }
2573
2574 /**
2575 * DatabaseInfo
2576 */
2577 // parse database status information and store it for all sql modules
load()2578 bool DatabaseInfo::load()
2579 {
2580 DDebug(&__plugin,DebugInfo,"DatabaseInfo::load() [%p] - loading data",this);
2581 String modules[] = {"pgsqldb", "mysqldb"};
2582 unsigned int i = 0;
2583
2584 for (int i = 0; i < FailedConns; i++)
2585 m_connData[i] = 0;
2586
2587 while (i < 2) {
2588 Message msg("engine.status");
2589 msg.addParam("module",modules[i]);
2590 msg.addParam("details","false");
2591 Engine::dispatch(msg);
2592 String& status = msg.retValue();
2593 if (!TelEngine::null(status)) {
2594 cutNewLine(status);
2595 /* status example: name=mysqldb,type=database,format=Total|Failed|Errors|AvgExecTime;conns=1,failed=1 */
2596 int pos = status.rfind(';');
2597 if (pos < 0)
2598 continue;
2599 String connInfo = status.substr(pos + 1);
2600 if (connInfo.null())
2601 continue;
2602 ObjList* l = connInfo.split(',');
2603 for (ObjList* j = l->skipNull(); j; j = j->skipNext()) {
2604 String* str = static_cast<String*>(j->get());
2605 pos = str->find("=");
2606 if (pos < 0)
2607 continue;
2608 String param = str->substr(0,pos);
2609 String value = str->substr(pos + 1);
2610 int type = lookup(param,s_databaseInfo,0);
2611 if (!type)
2612 continue;
2613 m_connData[type - 1] += value.toInteger();
2614 }
2615 TelEngine::destruct(l);
2616 }
2617 i++;
2618 }
2619 updateExpire();
2620 return true;
2621 }
2622
2623 // get the answer to a query for the entry with the given index
getInfo(const String & query,unsigned int & index,TokenDict * dict)2624 String DatabaseInfo::getInfo(const String& query, unsigned int& index, TokenDict* dict)
2625 {
2626 DDebug(&__plugin,DebugAll,"DatabaseInfo::getInfo(query='%s',index='%d',dict='%p') [%p]",query.c_str(),index,dict,this);
2627 // handle a query about an entry in the table
2628 Lock l(this);
2629 int type = lookup(query,s_dbAccountQueries,0);
2630 if (type) {
2631 if (index == 0 || index > m_table.count())
2632 return "";
2633 DatabaseAccount* acc = static_cast<DatabaseAccount*>(m_table[index - 1]);
2634 if (!acc)
2635 return "";
2636 return acc->getInfo(type);
2637 }
2638 if (!isExpired())
2639 // if the data has not yet expired, update the expire time
2640 updateExpire();
2641 // if the is no data available, obtain it from an engine.status message
2642 if (m_reload && !load())
2643 return "";
2644 // handle queries about connections
2645 type = lookup(query,s_databaseQuery,0);
2646 switch (type) {
2647 case Accounts:
2648 return String(m_table.count());
2649 case Connections:
2650 case FailedConns:
2651 return String(m_connData[type - 1]);
2652 default:
2653 break;
2654 }
2655 return "";
2656 }
2657
reset()2658 void DatabaseInfo::reset()
2659 {
2660 Lock l(this);
2661 for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
2662 DatabaseAccount* acc = static_cast<DatabaseAccount*>(o->get());
2663 acc->reset();
2664 }
2665 }
2666
2667 // create a new DatabaseAccount object for monitoring a database
addDatabase(NamedList * cfg)2668 void DatabaseInfo::addDatabase(NamedList* cfg)
2669 {
2670 if (!cfg || !m_monitor)
2671 return;
2672 DDebug(&__plugin,DebugInfo,"DatabaseInfo::addDatabase('%s') [%p]",cfg->c_str(),this);
2673 lock();
2674 DatabaseAccount* acc = static_cast<DatabaseAccount*>(m_table[*cfg]);
2675 if (!acc) {
2676 acc = new DatabaseAccount(cfg);;
2677 m_table.append(acc);
2678 acc->setIndex(m_table.count());
2679 }
2680 else
2681 acc->updateConfig(cfg);
2682 unlock();
2683 }
2684
updateDatabaseAccounts()2685 void DatabaseInfo::updateDatabaseAccounts()
2686 {
2687 lock();
2688 bool deletedRoute = true;
2689 while (deletedRoute) {
2690 deletedRoute = false;
2691 for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
2692 DatabaseAccount* acc = static_cast<DatabaseAccount*>(o->get());
2693 if (!acc->isCurrent()) {
2694 DDebug(__plugin.name(),DebugAll,"DatabaseInfo::updateDatabaseAccounts() - removed database account '%s' from monitoring",
2695 acc->toString().c_str());
2696 m_table.remove(acc);
2697 deletedRoute = true;
2698 }
2699 }
2700 }
2701 unsigned int index = 1;
2702 for (ObjList* o = m_table.skipNull(); o; o = o->skipNext()) {
2703 DatabaseAccount* acc = static_cast<DatabaseAccount*>(o->get());
2704 acc->setIsCurrent(false);
2705 acc->setIndex(index);
2706 index++;
2707 }
2708 unlock();
2709 }
2710
2711 //update the information for a given account
update(const Message & msg)2712 void DatabaseInfo::update(const Message& msg)
2713 {
2714 XDebug(&__plugin,DebugInfo,"DatabaseInfo::update()");
2715 int count = msg.getIntValue("count",0);
2716 for (int i = 0; i < count; i++) {
2717 String str = s_dbParam;
2718 str << i;
2719 String acc = msg.getValue(str);
2720 DatabaseAccount* dbAccount = static_cast<DatabaseAccount*>(m_table[acc]);
2721 if (!dbAccount)
2722 continue;
2723 NamedList nl(acc);
2724 str = s_totalParam;
2725 str << i;
2726 nl.setParam("total",msg.getValue(str));
2727 str = s_failedParam;
2728 str << i;
2729 nl.setParam("failed",msg.getValue(str));
2730 str = s_errorParam;
2731 str << i;
2732 nl.setParam("errorred",msg.getValue(str));
2733 str = s_hasConnParam;
2734 str << i;
2735 nl.setParam("hasconn",msg.getValue(str));
2736 str = s_timeParam;
2737 str << i;
2738 nl.setParam("querytime",msg.getValue(str));
2739 dbAccount->update(nl);
2740 }
2741 }
2742
2743 /**
2744 * RTPEntry
2745 */
RTPEntry(String rtpDirection)2746 RTPEntry::RTPEntry(String rtpDirection)
2747 : m_rtpDir(rtpDirection), m_index(0),
2748 m_isCurrent(true)
2749 {
2750 Debug(&__plugin,DebugAll,"RTPEntry '%s' created [%p]",rtpDirection.c_str(),this);
2751 reset();
2752 }
2753
~RTPEntry()2754 RTPEntry::~RTPEntry()
2755 {
2756 Debug(&__plugin,DebugAll,"RTPEntry '%s' destroyed [%p]",m_rtpDir.c_str(),this);
2757 }
2758
2759 // update the RTP info from the given list
update(const NamedList & nl)2760 void RTPEntry::update(const NamedList& nl)
2761 {
2762 DDebug(&__plugin,DebugAll,"RTPEntry::update() name='%s' [%p]",m_rtpDir.c_str(),this);
2763 for (unsigned int i = 0; i < nl.count(); i++) {
2764 NamedString* n = nl.getParam(i);
2765 if (!n)
2766 continue;
2767 int type = lookup(n->name(),s_rtpInfo,0);
2768 if (!type || type < NoAudio)
2769 continue;
2770 m_counters[type - NoAudio] += (*n).toInteger();
2771 }
2772 }
2773
2774 // reset counters
reset()2775 void RTPEntry::reset()
2776 {
2777 DDebug(&__plugin,DebugAll,"RTPEntry::reset() '%s' [%p]",m_rtpDir.c_str(),this);
2778 for (int i = 0; i < WrongSSRC - Direction; i++)
2779 m_counters[i] = 0;
2780 }
2781
2782 // the the answer to a query about this RTP direction
getInfo(unsigned int query)2783 String RTPEntry::getInfo(unsigned int query)
2784 {
2785 DDebug(&__plugin,DebugAll,"RTPEntry::getInfo('%s') '%s' [%p]",lookup(query,s_rtpQuery,""),m_rtpDir.c_str(),this);
2786 String retStr = "";
2787 switch (query) {
2788 case Direction:
2789 retStr << m_rtpDir;
2790 break;
2791 case Index:
2792 retStr << m_index;
2793 break;
2794 case NoAudio:
2795 case LostAudio:
2796 case PktsLost:
2797 case SyncLost:
2798 case SeqLost:
2799 case WrongSRC:
2800 case WrongSSRC:
2801 retStr << m_counters[query - NoAudio];
2802 break;
2803 default:
2804 break;
2805 }
2806 return retStr;
2807 }
2808
2809 /**
2810 * RTPTable
2811 */
RTPTable(const NamedList * cfg)2812 RTPTable::RTPTable(const NamedList* cfg)
2813 : m_rtpMtx(false,"Monitor::rtpInfo"),
2814 m_resetInterval(3600), m_monitor(false)
2815 {
2816 Debug(&__plugin,DebugAll,"RTPTable created [%p]",this);
2817 // build RTPEntry objects for monitoring RTP directions if monitoring is enabled
2818 reconfigure(cfg);
2819 }
2820
reconfigure(const NamedList * cfg)2821 void RTPTable::reconfigure(const NamedList* cfg)
2822 {
2823 if (!cfg)
2824 return;
2825 m_monitor = cfg->getBoolValue("monitor",false);
2826 m_resetInterval = cfg->getIntValue("reset_interval",3600);
2827
2828 m_rtpMtx.lock();
2829 if (!m_monitor)
2830 m_rtpEntries.clear();
2831 String directions = cfg->getValue("rtp_directions","");
2832 Debug(&__plugin,DebugAll,"RTPTable [%p] configured with directions='%s',resetTime=" FMT64U,this,directions.c_str(),m_resetInterval);
2833 if (m_monitor) {
2834 ObjList* l = directions.split(',');
2835 for (ObjList* o = l->skipNull(); o; o = o->skipNext()) {
2836 String* str = static_cast<String*>(o->get());
2837 RTPEntry* entry = static_cast<RTPEntry*>(m_rtpEntries[*str]);
2838 if (!entry) {
2839 entry = new RTPEntry(*str);
2840 m_rtpEntries.append(entry);
2841 entry->setIndex(m_rtpEntries.count());
2842 }
2843 else
2844 entry->setIsCurrent(true);
2845 }
2846 TelEngine::destruct(l);
2847 }
2848
2849 bool deletedDir = true;
2850 while (deletedDir) {
2851 deletedDir = false;
2852 for (ObjList* o = m_rtpEntries.skipNull(); o; o = o->skipNext()) {
2853 RTPEntry* entry = static_cast<RTPEntry*>(o->get());
2854 if (!entry->isCurrent()) {
2855 DDebug(__plugin.name(),DebugAll,"RTPTable::reconfigure() - removed direction '%s' from monitoring",entry->toString().c_str());
2856 m_rtpEntries.remove(entry);
2857 deletedDir = true;
2858 }
2859 }
2860 }
2861 unsigned int index = 1;
2862 for (ObjList* o = m_rtpEntries.skipNull(); o; o = o->skipNext()) {
2863 RTPEntry* entry = static_cast<RTPEntry*>(o->get());
2864 entry->setIsCurrent(false);
2865 entry->setIndex(index);
2866 index++;
2867 }
2868
2869 m_rtpMtx.unlock();
2870 m_resetTime = Time::secNow() + m_resetInterval;
2871 }
2872
2873 // update an entry
update(Message & msg)2874 void RTPTable::update(Message& msg)
2875 {
2876 XDebug(&__plugin,DebugAll,"RTPTable::update() [%p]",this);
2877 String dir = lookup(RTPEntry::Direction,RTPEntry::s_rtpInfo,"");
2878 if (dir.null())
2879 dir = "remoteip";
2880 String rtpDir = msg.getValue(dir,"");
2881 if (rtpDir.null())
2882 return;
2883 m_rtpMtx.lock();
2884 RTPEntry* entry = static_cast<RTPEntry*>(m_rtpEntries[rtpDir]);
2885 if (entry)
2886 entry->update(msg);
2887 m_rtpMtx.unlock();
2888 }
2889
getInfo(const String & query,const unsigned int & index)2890 String RTPTable::getInfo(const String& query, const unsigned int& index)
2891 {
2892 DDebug(&__plugin,DebugAll,"RTPTable::getInfo(query='%s',index='%u') [%p]",query.c_str(),index,this);
2893 String retStr = "";
2894 int type = lookup(query,s_rtpQuery,0);
2895 if (!type)
2896 return retStr;
2897 if (type == RTPEntry::Count)
2898 retStr << m_rtpEntries.count();
2899 else if (index > 0 && index <= m_rtpEntries.count()) {
2900 m_rtpMtx.lock();
2901 RTPEntry* entry = static_cast<RTPEntry*>(m_rtpEntries[index - 1]);
2902 if (entry)
2903 retStr << entry->getInfo(type);
2904 m_rtpMtx.unlock();
2905 }
2906 return retStr;
2907 }
2908
2909 // reset information
reset()2910 void RTPTable::reset()
2911 {
2912 XDebug(&__plugin,DebugAll,"RTPTable::reset() [%p]",this);
2913 m_rtpMtx.lock();
2914 for (ObjList* o = m_rtpEntries.skipNull(); o; o = o->skipNext()) {
2915 RTPEntry* e = static_cast<RTPEntry*>(o->get());
2916 e->reset();
2917 }
2918 m_rtpMtx.unlock();
2919 m_resetTime = Time::secNow() + m_resetInterval;
2920 }
2921
2922 /**
2923 * CallRouteQoS
2924 */
CallRouteQoS(const String direction,const NamedList * cfg)2925 CallRouteQoS::CallRouteQoS(const String direction, const NamedList* cfg)
2926 : m_routeName(direction)
2927 {
2928 Debug(&__plugin,DebugAll,"CallRouteQoS [%p] created for route '%s',cfg='%p'",this,direction.c_str(),cfg);
2929 for (int i = 0; i < NO_CAUSE - HANGUP; i++) {
2930 m_callCounters[i] = 0;
2931 m_callCountersAll[i] = 0;
2932 }
2933
2934 for (int i = 0; i <= TOTAL_IDX; i++) {
2935 m_totalCalls[i] = 0;
2936 m_answeredCalls[i] = 0;
2937 m_delivCalls[i] = 0;
2938 }
2939
2940 for (int i = 0; i <= NER_LOW_ALL; i++)
2941 m_alarmCounters[i] = 0;
2942
2943 m_alarms = 0;
2944 m_overallAlarms = 0;
2945
2946 m_alarmsSent = 0;
2947 m_overallAlarmsSent = 0;
2948
2949 m_minCalls = 1;
2950 m_minASR = m_maxASR = m_minNER = -1;
2951 if (cfg)
2952 updateConfig(cfg);
2953 m_index = 0;
2954 }
2955
~CallRouteQoS()2956 CallRouteQoS::~CallRouteQoS()
2957 {
2958 Debug(&__plugin,DebugAll,"CallRouteQoS [%p] destroyed",this);
2959 }
2960
updateConfig(const NamedList * cfg)2961 void CallRouteQoS::updateConfig(const NamedList* cfg)
2962 {
2963 if (!cfg)
2964 return;
2965 m_minCalls = cfg->getIntValue("mincalls",m_minCalls);
2966 m_minASR = cfg->getIntValue("minASR",m_minASR);
2967 if (m_minASR > 100 || m_minASR < -1) {
2968 Debug(&__plugin,DebugNote,"CallRouteQoS::updateConfig() - route '%s': configured minASR is not in the -1..100 interval, "
2969 "defaulting to -1",m_routeName.c_str());
2970 m_minASR = -1;
2971 }
2972 m_maxASR = cfg->getIntValue("maxASR",m_maxASR);
2973 if (m_maxASR > 100 || m_maxASR < -1) {
2974 Debug(&__plugin,DebugNote,"CallRouteQoS::updateConfig() - route '%s': configured maxASR is not in the -1..100 interval, "
2975 "defaulting to -1",m_routeName.c_str());
2976 m_maxASR = -1;
2977 }
2978 m_minNER = cfg->getIntValue("minNER",m_minNER);
2979 if (m_minNER > 100 || m_minNER < -1) {
2980 Debug(&__plugin,DebugNote,"CallRouteQoS::updateConfig() - route '%s': configured minNER is not in the -1..100 interval, "
2981 "defaulting to -1",m_routeName.c_str());
2982 m_minNER = -1;
2983 }
2984 m_isCurrent = true;
2985 }
2986
2987 // update the counters taking into account the type of the call and reason with which the call was ended
update(int type,int endReason)2988 void CallRouteQoS::update(int type, int endReason)
2989 {
2990 DDebug(&__plugin,DebugAll,"CallRouteQoS::update(callType='%d',endReason='%d') [%p] ",type,endReason,this);
2991 m_totalCalls[CURRENT_IDX]++;
2992 m_totalCalls[TOTAL_IDX]++;
2993 switch (type) {
2994 case ANSWERED:
2995 m_answeredCalls[CURRENT_IDX]++;
2996 m_answeredCalls[TOTAL_IDX]++;
2997 break;
2998 case DELIVERED:
2999 m_delivCalls[CURRENT_IDX]++;
3000 m_delivCalls[TOTAL_IDX]++;
3001 break;
3002 default:
3003 break;
3004 }
3005 if (endReason != -1 && endReason >= HANGUP && endReason < NO_CAUSE) {
3006 m_callCounters[endReason - HANGUP]++;
3007 m_callCountersAll[endReason - HANGUP]++;
3008 }
3009 }
3010
3011 // update the internal ASR/NER values and check for alarms
updateQoS()3012 void CallRouteQoS::updateQoS()
3013 {
3014 //XDebug(&__plugin,DebugAll,"CallRouteQoS::updateQoS() [%p]",this);
3015 int realASR, totalASR;
3016 if ((m_totalCalls[CURRENT_IDX] != m_totalCalls[PREVIOUS_IDX]) && (m_totalCalls[CURRENT_IDX] >= m_minCalls)) {
3017
3018 float currentHyst = 50.0 / m_totalCalls[CURRENT_IDX] * s_qosHysteresisFactor;
3019 float totalHyst = 50.0 / m_totalCalls[TOTAL_IDX] * s_qosHysteresisFactor;
3020
3021 realASR = (int) (m_answeredCalls[CURRENT_IDX] * 100.0 / m_totalCalls[CURRENT_IDX]);
3022 checkForAlarm(realASR,currentHyst,m_alarms,m_minASR,m_maxASR,LOW_ASR,HIGH_ASR);
3023 m_totalCalls[PREVIOUS_IDX] = m_totalCalls[CURRENT_IDX];
3024
3025 totalASR = (int) (m_answeredCalls[TOTAL_IDX] * 100.0 / m_totalCalls[TOTAL_IDX]);
3026 checkForAlarm(totalASR,totalHyst,m_overallAlarms,m_minASR,m_maxASR,LOW_ASR,HIGH_ASR);
3027
3028 int ner = (int) ((m_answeredCalls[CURRENT_IDX] + m_delivCalls[CURRENT_IDX]) * 100.0 / m_totalCalls[CURRENT_IDX]);
3029 checkForAlarm(ner,currentHyst,m_alarms,m_minNER,-1,LOW_NER);
3030
3031 ner = (int) ((m_answeredCalls[TOTAL_IDX] + m_delivCalls[TOTAL_IDX]) * 100.0 / m_totalCalls[TOTAL_IDX]);
3032 checkForAlarm(ner,totalHyst,m_overallAlarms,m_minNER,-1,LOW_NER);
3033 }
3034 }
3035
3036 // reset counters
reset()3037 void CallRouteQoS::reset()
3038 {
3039 DDebug(&__plugin,DebugInfo,"CallRoute::reset() [%p]",this);
3040 m_totalCalls[CURRENT_IDX] = m_totalCalls[PREVIOUS_IDX] = 0;
3041 m_answeredCalls[CURRENT_IDX] = m_answeredCalls[PREVIOUS_IDX] = 0;
3042 m_delivCalls[CURRENT_IDX] = m_delivCalls[PREVIOUS_IDX] = 0;
3043 m_alarms = 0;
3044 m_alarmsSent = 0;
3045 m_alarmCounters[ASR_LOW] = m_alarmCounters[ASR_HIGH] = m_alarmCounters[NER_LOW] = 0;
3046 for (int i = 0; i < NO_CAUSE - HANGUP; i++)
3047 m_callCounters[i] = 0;
3048 }
3049
3050 // check a value against a threshold and if necessary set an alarm
checkForAlarm(int & value,float hysteresis,u_int8_t & alarm,const int min,const int max,u_int8_t minAlarm,u_int8_t maxAlarm)3051 void CallRouteQoS::checkForAlarm(int& value, float hysteresis, u_int8_t& alarm, const int min,
3052 const int max, u_int8_t minAlarm, u_int8_t maxAlarm)
3053 {
3054 if (min >= 0) {
3055 float hystValue = (alarm & minAlarm ? value - hysteresis : value + hysteresis);
3056 if (hystValue <= min)
3057 alarm |= minAlarm;
3058 else
3059 alarm &= ~minAlarm;
3060 }
3061 if (max >= 0) {
3062 float hystValue = (alarm & maxAlarm ? value + hysteresis : value - hysteresis);
3063 if (hystValue >= max)
3064 alarm |= maxAlarm;
3065 else
3066 alarm &= ~maxAlarm;
3067 }
3068 }
3069
3070 // is this entry in an alarm state?
alarm()3071 bool CallRouteQoS::alarm()
3072 {
3073 if (m_alarms || m_overallAlarms)
3074 return true;
3075 m_alarmsSent = m_overallAlarmsSent = 0;
3076 return false;
3077 }
3078
3079 // get the string version of the alarm and remember that we sent this alarm (avoid sending the same alarm multiple times)
alarmText()3080 const String CallRouteQoS::alarmText()
3081 {
3082 String text = "";
3083 if (m_alarms & LOW_ASR) {
3084 if (!(m_alarmsSent & LOW_ASR)) {
3085 m_alarmsSent |= LOW_ASR;
3086 m_alarmCounters[ASR_LOW]++;
3087 return text = lookup(ASR_LOW,s_callQualityQueries,"");
3088 }
3089 }
3090 else
3091 m_alarmsSent &= ~LOW_ASR;
3092
3093 if (m_alarms & HIGH_ASR) {
3094 if (!(m_alarmsSent & HIGH_ASR)) {
3095 m_alarmsSent |= HIGH_ASR;
3096 m_alarmCounters[ASR_HIGH]++;
3097 return text = lookup(ASR_HIGH,s_callQualityQueries,"");
3098 }
3099 }
3100 else
3101 m_alarmsSent &= ~HIGH_ASR;
3102
3103 if (m_alarms & LOW_NER) {
3104 if (!(m_alarmsSent & LOW_NER)) {
3105 m_alarmsSent |= LOW_NER;
3106 m_alarmCounters[NER_LOW]++;
3107 return text = lookup(NER_LOW,s_callQualityQueries,"");
3108 }
3109 }
3110 else
3111 m_alarmsSent &= ~LOW_NER;
3112
3113 if (m_overallAlarms & LOW_ASR) {
3114 if (!(m_overallAlarmsSent & LOW_ASR)) {
3115 m_overallAlarmsSent |= LOW_ASR;
3116 m_alarmCounters[ASR_LOW_ALL]++;
3117 return text = lookup(ASR_LOW_ALL,s_callQualityQueries,"");
3118 }
3119 }
3120 else
3121 m_overallAlarmsSent &= ~LOW_ASR;
3122
3123 if (m_overallAlarms & HIGH_ASR) {
3124 if (!(m_overallAlarmsSent & HIGH_ASR)) {
3125 m_overallAlarmsSent |= HIGH_ASR;
3126 m_alarmCounters[ASR_HIGH_ALL]++;
3127 return text = lookup(ASR_HIGH_ALL,s_callQualityQueries,"");
3128 }
3129 }
3130 else
3131 m_overallAlarmsSent &= ~HIGH_ASR;
3132
3133 if (m_overallAlarms & LOW_NER) {
3134 if (!(m_overallAlarmsSent & LOW_NER)) {
3135 m_overallAlarmsSent |= LOW_NER;
3136 m_alarmCounters[NER_LOW_ALL]++;
3137 return text = lookup(NER_LOW_ALL,s_callQualityQueries,"");
3138 }
3139 }
3140 else
3141 m_overallAlarmsSent &= ~LOW_NER;
3142 return text;
3143 }
3144
3145 // send periodical notification containing current ASR and NER values
sendNotifs(unsigned int index,bool rst)3146 void CallRouteQoS::sendNotifs(unsigned int index, bool rst)
3147 {
3148 DDebug(&__plugin,DebugInfo,"CallRouteQoS::sendNotifs() - route='%s' reset=%s [%p]",toString().c_str(),String::boolText(rst),this);
3149 // we dont want notifcations if we didn't have the minimum number of calls
3150 if (m_totalCalls[CURRENT_IDX] >= m_minCalls) {
3151 NamedList nl("");
3152 String value;
3153 nl.addParam("index",String(index));
3154 nl.addParam("count","4");
3155
3156 for (int i = 0; i < 4; i++) {
3157 String param = "notify.";
3158 param << i;
3159 String paramValue = "value.";
3160 paramValue << i;
3161 nl.addParam(param,lookup(ASR + i,s_callQualityQueries,""));
3162 get(ASR + i,value);
3163 nl.addParam(paramValue,value);
3164 }
3165 __plugin.sendTraps(nl);
3166 }
3167 if (rst)
3168 reset();
3169 }
3170
3171 // get the value for a query
get(int query,String & result)3172 bool CallRouteQoS::get(int query, String& result)
3173 {
3174 DDebug(&__plugin,DebugInfo,"CallRouteQoS::get(query='%s') [%p]",lookup(query,s_callQualityQueries,""),this);
3175 int val = 0;
3176 if (query) {
3177 switch (query) {
3178 case ASR:
3179 if (m_totalCalls[CURRENT_IDX]) {
3180 val = (int) (m_answeredCalls[CURRENT_IDX] * 100.0 / m_totalCalls[CURRENT_IDX]);
3181 result = String( val);
3182 }
3183 else
3184 result = "-1";
3185 return true;
3186 case NER:
3187 if (m_totalCalls[CURRENT_IDX]) {
3188 val = (int) ((m_answeredCalls[CURRENT_IDX] + m_delivCalls[CURRENT_IDX]) * 100.0 / m_totalCalls[CURRENT_IDX]);
3189 result = String(val);
3190 }
3191 else
3192 result = "-1";
3193 return true;
3194 case ASR_ALL:
3195 if (m_totalCalls[TOTAL_IDX]) {
3196 val = (int) (m_answeredCalls[TOTAL_IDX] * 100.0 / m_totalCalls[TOTAL_IDX]);
3197 result = String(val);
3198 }
3199 else
3200 result = "-1";
3201 return true;
3202 case NER_ALL:
3203 if (m_totalCalls[TOTAL_IDX]) {
3204 val = (int) ((m_answeredCalls[TOTAL_IDX] + m_delivCalls[TOTAL_IDX]) * 100.0 / m_totalCalls[TOTAL_IDX]);
3205 result = String(val);
3206 }
3207 else
3208 result = "-1";
3209 return true;
3210 case MIN_ASR:
3211 result << m_minASR;
3212 return true;
3213 case MAX_ASR:
3214 result << m_maxASR;
3215 return true;
3216 case MIN_NER:
3217 result << m_minNER;
3218 return true;
3219 case LOW_ASR_COUNT:
3220 case HIGH_ASR_COUNT:
3221 case LOW_ASR_ALL_COUNT:
3222 case HIGH_ASR_ALL_COUNT:
3223 case LOW_NER_COUNT:
3224 case LOW_NER_ALL_COUNT:
3225 result << m_alarmCounters[query - LOW_ASR_COUNT + 1];
3226 return true;
3227 case HANGUP:
3228 case REJECT:
3229 case BUSY:
3230 case CANCELLED:
3231 case NO_ANSWER:
3232 case NO_ROUTE:
3233 case NO_CONN:
3234 case NO_AUTH:
3235 case CONGESTION:
3236 case NO_MEDIA:
3237 result << m_callCounters[query - HANGUP];
3238 return true;
3239 case HANGUP_ALL:
3240 case REJECT_ALL:
3241 case BUSY_ALL:
3242 case CANCELLED_ALL:
3243 case NO_ANSWER_ALL:
3244 case NO_ROUTE_ALL:
3245 case NO_CONN_ALL:
3246 case NO_AUTH_ALL:
3247 case CONGESTION_ALL:
3248 case NO_MEDIA_ALL:
3249 result << m_callCountersAll[query - HANGUP_ALL];
3250 return true;
3251 case NAME:
3252 result << toString();
3253 return true;
3254 case INDEX:
3255 result << m_index;
3256 return true;
3257 default:
3258 return false;
3259 }
3260 }
3261 return false;
3262 }
3263
3264 /**
3265 * CallMonitor
3266 */
CallMonitor(const NamedList * sect,unsigned int priority)3267 CallMonitor::CallMonitor(const NamedList* sect, unsigned int priority)
3268 : MessageHandler("call.cdr",priority),
3269 Thread("Call Monitor"),
3270 m_checkTime(3600),
3271 m_notifTime(0), m_inCalls(0), m_outCalls(0), m_first(true),
3272 m_routeParam("address"),
3273 m_monitor(false)
3274 {
3275 setFilter("operation","finalize");
3276 // configure
3277 setConfigure(sect);
3278
3279 m_notifTime = Time::secNow() + m_checkTime;
3280 init();
3281 }
3282
init()3283 bool CallMonitor::init()
3284 {
3285 return startup();
3286 }
3287
3288 // main loop. Update the monitor data, if neccessary, send alarms and/or notifications
run()3289 void CallMonitor::run()
3290 {
3291 for (;;) {
3292 check();
3293 idle();
3294 bool sendNotif = false;
3295
3296 m_cfgMtx.lock();
3297 if (!m_first && Time::secNow() >= m_notifTime) {
3298 m_notifTime = Time::secNow() + m_checkTime;
3299 sendNotif = true;
3300 }
3301 m_cfgMtx.unlock();
3302
3303 m_routesMtx.lock();
3304 unsigned int index = 0;
3305 for (ObjList* o = m_routes.skipNull(); o; o = o->skipNext()) {
3306 index++;
3307 CallRouteQoS* route = static_cast<CallRouteQoS*>(o->get());
3308 route->updateQoS();
3309 if (route->alarm())
3310 sendAlarmFrom(route);
3311 if (sendNotif)
3312 route->sendNotifs(index,true);
3313 }
3314 if (sendNotif)
3315 sendNotif = false;
3316
3317 if (m_first)
3318 m_first = false;
3319 m_routesMtx.unlock();
3320 }
3321 }
3322
3323 // set configuration
setConfigure(const NamedList * sect)3324 void CallMonitor::setConfigure(const NamedList* sect)
3325 {
3326 if (!sect)
3327 return;
3328 m_cfgMtx.lock();
3329 m_checkTime = sect ? sect->getIntValue("time_interval",3600) : 3600;
3330 m_routeParam = sect ? sect->getValue("route","address") : "address";
3331 m_monitor = sect ? sect->getBoolValue("monitor",false) : false;
3332 if (!m_monitor)
3333 m_routes.clear();
3334
3335 // if the previous time for notification is later than the one with the new interval, reset it
3336 if (m_notifTime > Time::secNow() + m_checkTime)
3337 m_notifTime = Time::secNow() + m_checkTime;
3338
3339 s_qosHysteresisFactor = sect ? sect->getDoubleValue("hysteresis_factor",2.0) : 2.0;
3340 if (s_qosHysteresisFactor > 10 || s_qosHysteresisFactor < 1.0) {
3341 Debug(&__plugin,DebugNote,"CallMonitor::setConfigure() - configured hysteresis_factor is not in the 1.0 - 10.0 interval,"
3342 " defaulting to 2.0");
3343 s_qosHysteresisFactor = 2.0;
3344 }
3345 m_cfgMtx.unlock();
3346 }
3347
3348 // add a route to be monitored
addRoute(NamedList * cfg)3349 void CallMonitor::addRoute(NamedList* cfg)
3350 {
3351 if (!m_monitor || !cfg)
3352 return;
3353 m_routesMtx.lock();
3354 CallRouteQoS* route = static_cast<CallRouteQoS*>(m_routes[*cfg]);
3355 if (!route) {
3356 route = new CallRouteQoS(*cfg,cfg);
3357 m_routes.append(route);
3358 route->setIndex(m_routes.count());
3359 }
3360 else
3361 route->updateConfig(cfg);
3362 m_routesMtx.unlock();
3363 }
3364
updateRoutes()3365 void CallMonitor::updateRoutes()
3366 {
3367 m_routesMtx.lock();
3368 bool deletedRoute = true;
3369 while (deletedRoute) {
3370 deletedRoute = false;
3371 for (ObjList* o = m_routes.skipNull(); o; o = o->skipNext()) {
3372 CallRouteQoS* route = static_cast<CallRouteQoS*>(o->get());
3373 if (!route->isCurrent()) {
3374 DDebug(__plugin.name(),DebugAll,"CallMonitor::updateRoutes() - removed route '%s' from monitoring",route->toString().c_str());
3375 m_routes.remove(route);
3376 deletedRoute = true;
3377 }
3378 }
3379 }
3380 unsigned int index = 1;
3381 for (ObjList* o = m_routes.skipNull(); o; o = o->skipNext()) {
3382 CallRouteQoS* route = static_cast<CallRouteQoS*>(o->get());
3383 route->setIsCurrent(false);
3384 route->setIndex(index);
3385 index++;
3386 }
3387 m_routesMtx.unlock();
3388 }
3389
3390 // send an alarm received from a route
sendAlarmFrom(CallRouteQoS * route)3391 void CallMonitor::sendAlarmFrom(CallRouteQoS* route)
3392 {
3393 if (!route)
3394 return;
3395 String alarm = route->alarmText();
3396 if (!alarm.null())
3397 __plugin.sendTrap(alarm,route->toString());
3398 }
3399
3400 // extract from a call.cdr message call information and update the monitor data
received(Message & msg)3401 bool CallMonitor::received(Message& msg)
3402 {
3403 DDebug(__plugin.name(),DebugAll,"CdrHandler::received()");
3404
3405 if (m_routeParam.null())
3406 return false;
3407 String routeStr = msg.getValue(m_routeParam);
3408 if (routeStr.null())
3409 return false;
3410 if (m_routeParam == YSTRING("address")) {
3411 int pos = routeStr.rfind(':');
3412 if (pos < 0)
3413 pos = routeStr.rfind('/');
3414 if (pos > 0)
3415 routeStr = routeStr.substr(0,pos);
3416 }
3417
3418 const String& status = msg[YSTRING("status")];
3419 int code = -1;
3420 if (status == YSTRING("answered"))
3421 code = CallRouteQoS::ANSWERED;
3422 else if (status == YSTRING("ringing") || status == YSTRING("accepted"))
3423 code = CallRouteQoS::DELIVERED;
3424
3425 const String& direction = msg[YSTRING("direction")];
3426 bool outgoing = false;
3427 if (msg.getBoolValue("cdrwrite",true)) {
3428 if (direction == YSTRING("incoming"))
3429 m_inCalls++;
3430 else if (direction == YSTRING("outgoing")) {
3431 outgoing = true;
3432 m_outCalls++;
3433 }
3434 }
3435
3436 const String& reason = msg[YSTRING("reason")];
3437 int type = lookup(reason,s_endReasons,CallRouteQoS::HANGUP);
3438 if (type == CallRouteQoS::HANGUP && code == CallRouteQoS::DELIVERED && outgoing)
3439 type = CallRouteQoS::CANCELLED;
3440 else if (type <= CallRouteQoS::NO_ANSWER && !outgoing)
3441 type = CallRouteQoS::HANGUP;
3442
3443 m_routesMtx.lock();
3444 CallRouteQoS* route = static_cast<CallRouteQoS*>(m_routes[routeStr]);
3445 if (route)
3446 route->update(code,type);
3447 m_routesMtx.unlock();
3448 return false;
3449 }
3450
3451 // get a value from the call counters
getCounter(int type,unsigned int & value)3452 bool CallMonitor::getCounter(int type, unsigned int& value)
3453 {
3454 DDebug(__plugin.name(),DebugAll,"CallMonitor::getCounter(%s)",lookup(type,s_callCounterQueries,""));
3455 if (type == 0 || type > ROUTES_COUNT)
3456 return false;
3457 switch (type) {
3458 case INCOMING_CALLS:
3459 value = m_inCalls;
3460 break;
3461 case OUTGOING_CALLS:
3462 value = m_outCalls;
3463 break;
3464 case ROUTES_COUNT:
3465 value = m_routes.count();
3466 break;
3467 default:
3468 return false;
3469 }
3470 return true;
3471 }
3472
3473 // obtain call monitor specific data
get(const String & query,const int & index,String & result)3474 void CallMonitor::get(const String& query, const int& index, String& result)
3475 {
3476 DDebug(__plugin.name(),DebugAll,"CallMonitor::get(%s,%d)",query.c_str(),index);
3477 if (index > 0) {
3478 CallRouteQoS* route = static_cast<CallRouteQoS*>(m_routes[index - 1]);
3479 if (!route)
3480 return;
3481
3482 int type = lookup(query,s_callQualityQueries,0);
3483 if (type && route->get(type,result))
3484 return;
3485 }
3486 int type = lookup(query,s_callCounterQueries,0);
3487 unsigned int value = 0;
3488 if (getCounter(type,value)) {
3489 result += value;
3490 return;
3491 }
3492 }
3493
3494 /**
3495 * Monitor
3496 */
Monitor()3497 Monitor::Monitor()
3498 : Module("monitoring","misc"),
3499 m_msgUpdateHandler(0),
3500 m_snmpMsgHandler(0),
3501 m_hangupHandler(0),
3502 m_startHandler(0),
3503 m_callMonitor(0),
3504 m_authHandler(0),
3505 m_registerHandler(0),
3506 m_init(false),
3507 m_newTraps(false),
3508 m_sipMonitoredGws(0),
3509 m_trunkMon(false),
3510 m_linksetMon(false),
3511 m_linkMon(false),
3512 m_interfaceMon(false),
3513 m_isdnMon(false),
3514 m_activeCallsCache(0),
3515 m_trunkInfo(0),
3516 m_engineInfo(0),
3517 m_moduleInfo(0),
3518 m_dbInfo(0),
3519 m_rtpInfo(0),
3520 m_linksetInfo(0),
3521 m_linkInfo(0),
3522 m_ifaceInfo(0),
3523 m_accountsInfo(0)
3524 {
3525 Output("Loaded module Monitoring");
3526 }
3527
~Monitor()3528 Monitor::~Monitor()
3529 {
3530 Output("Unloaded module Monitoring");
3531
3532 Debugger::setAlarmHook();
3533
3534 TelEngine::destruct(m_moduleInfo);
3535 TelEngine::destruct(m_engineInfo);
3536 TelEngine::destruct(m_activeCallsCache);
3537 TelEngine::destruct(m_linkInfo);
3538 TelEngine::destruct(m_linksetInfo);
3539 TelEngine::destruct(m_trunkInfo);
3540 TelEngine::destruct(m_dbInfo);
3541 TelEngine::destruct(m_rtpInfo);
3542 TelEngine::destruct(m_ifaceInfo);
3543 TelEngine::destruct(m_accountsInfo);
3544 TelEngine::destruct(m_sipMonitoredGws);
3545
3546 TelEngine::destruct(m_msgUpdateHandler);
3547 TelEngine::destruct(m_snmpMsgHandler);
3548 TelEngine::destruct(m_startHandler);
3549 TelEngine::destruct(m_authHandler);
3550 TelEngine::destruct(m_registerHandler);
3551 TelEngine::destruct(m_hangupHandler);
3552 }
3553
unload()3554 bool Monitor::unload()
3555 {
3556 DDebug(this,DebugAll,"::unload()");
3557 if (!lock(500000))
3558 return false;
3559
3560 Engine::uninstall(m_msgUpdateHandler);
3561 Engine::uninstall(m_snmpMsgHandler);
3562 Engine::uninstall(m_startHandler);
3563 Engine::uninstall(m_authHandler);
3564 Engine::uninstall(m_registerHandler);
3565 Engine::uninstall(m_hangupHandler);
3566
3567 if (m_callMonitor) {
3568 Engine::uninstall(m_callMonitor);
3569 m_callMonitor->cancel();
3570 m_callMonitor = 0;
3571 }
3572
3573 uninstallRelays();
3574 unlock();
3575 return true;
3576 }
3577
initialize()3578 void Monitor::initialize()
3579 {
3580 Output("Initializing module Monitoring");
3581
3582 // read configuration
3583 Configuration cfg(Engine::configFile("monitoring"));
3584
3585 if (!m_init) {
3586 m_init = true;
3587 setup();
3588 installRelay(Halt);
3589 installRelay(Timer);
3590 Debugger::setAlarmHook(alarmCallback);
3591
3592 s_nodeState = "active";
3593 }
3594
3595 if (!m_msgUpdateHandler) {
3596 m_msgUpdateHandler = new MsgUpdateHandler();
3597 Engine::install(m_msgUpdateHandler);
3598 }
3599 if (!m_snmpMsgHandler) {
3600 m_snmpMsgHandler = new SnmpMsgHandler();
3601 Engine::install(m_snmpMsgHandler);
3602 }
3603 if (!m_hangupHandler) {
3604 m_hangupHandler = new HangupHandler();
3605 Engine::install(m_hangupHandler);
3606 }
3607 if (!m_startHandler) {
3608 m_startHandler = new EngineStartHandler();
3609 Engine::install(m_startHandler);
3610 }
3611 if (!m_authHandler) {
3612 m_authHandler = new AuthHandler();
3613 Engine::install(m_authHandler);
3614 }
3615 if (!m_registerHandler) {
3616 m_registerHandler = new RegisterHandler();
3617 Engine::install(m_registerHandler);
3618 }
3619
3620 // build a call monitor
3621 NamedList* asrCfg = cfg.getSection("call_qos");
3622 if (!m_callMonitor) {
3623 m_callMonitor = new CallMonitor(asrCfg);
3624 Engine::install(m_callMonitor);
3625 }
3626 else
3627 m_callMonitor->setConfigure(asrCfg);
3628
3629 int cacheFor = cfg.getIntValue("general","cache",1);
3630 if (!m_activeCallsCache)
3631 m_activeCallsCache = new ActiveCallsInfo();
3632 m_activeCallsCache->setRetainInfoTime(cacheFor);//seconds
3633
3634 if (!m_trunkInfo)
3635 m_trunkInfo = new TrunkInfo();
3636 m_trunkInfo->setRetainInfoTime(cacheFor);//seconds
3637
3638 if (!m_linksetInfo)
3639 m_linksetInfo = new LinksetInfo();
3640 m_linksetInfo->setRetainInfoTime(cacheFor);//seconds
3641
3642 if (!m_linkInfo)
3643 m_linkInfo = new LinkInfo();
3644 m_linkInfo->setRetainInfoTime(cacheFor);//seconds
3645
3646 if (!m_ifaceInfo)
3647 m_ifaceInfo = new InterfaceInfo();
3648 m_ifaceInfo->setRetainInfoTime(cacheFor);//seconds
3649
3650 if (!m_accountsInfo)
3651 m_accountsInfo = new AccountsInfo();
3652 m_accountsInfo->setRetainInfoTime(cacheFor);//seconds
3653
3654 if (!m_engineInfo)
3655 m_engineInfo = new EngineInfo();
3656 m_engineInfo->setRetainInfoTime(cacheFor);//seconds
3657
3658 if (!m_moduleInfo)
3659 m_moduleInfo = new ModuleInfo();
3660 m_moduleInfo->setRetainInfoTime(cacheFor);//seconds
3661
3662 bool enable = cfg.getBoolValue("database","monitor",false);
3663 if (!m_dbInfo)
3664 m_dbInfo = new DatabaseInfo(enable);
3665 else
3666 m_dbInfo->setMonitorEnabled(enable);
3667 m_dbInfo->setRetainInfoTime(cacheFor);
3668
3669 readConfig(cfg);
3670 }
3671
readConfig(const Configuration & cfg)3672 void Monitor::readConfig(const Configuration& cfg)
3673 {
3674 // get the threshold for yate restart alarm
3675 s_yateRunAlarm = cfg.getIntValue("general","restart_alarm",1);
3676 int level = cfg.getIntValue("general","alarm_threshold",DebugNote);
3677 if (level < DebugFail)
3678 level = -1;
3679 else if (level < DebugConf)
3680 level = DebugConf;
3681 else if (level > DebugAll)
3682 level = DebugAll;
3683 s_alarmThreshold = level;
3684 m_newTraps = !cfg.getBoolValue("general","old_trap_style");
3685
3686 // read configs for database monitoring (they type=database, the name of the section is the database account)
3687 for (unsigned int i = 0; i < cfg.sections(); i++) {
3688 NamedList* sec = cfg.getSection(i);
3689 if (!sec || (*sec == "general"))
3690 continue;
3691 String type = sec->getValue("type","");
3692 if (type.null())
3693 continue;
3694 if (type == "database" && m_dbInfo)
3695 m_dbInfo->addDatabase(sec);
3696 if (type == "call_qos" && m_callMonitor)
3697 m_callMonitor->addRoute(sec);
3698 }
3699 m_callMonitor->updateRoutes();
3700 m_dbInfo->updateDatabaseAccounts();
3701
3702 // read config for SIP monitoring
3703 String gw = cfg.getValue("sip","gateways","");
3704 if (!gw.null()) {
3705 if (m_sipMonitoredGws)
3706 TelEngine::destruct(m_sipMonitoredGws);
3707 m_sipMonitoredGws = gw.split(';',false);
3708 for (ObjList* o = m_sipMonitoredGws->skipNull(); o; o = o->skipNext()) {
3709 String* addr = static_cast<String*>(o->get());
3710 int pos = addr->find(":");
3711 if (pos == -1)
3712 addr->append(":" + String(SIP_PORT));
3713 else {
3714 String tmp = addr->substr(pos+1);
3715 if (tmp.null())
3716 addr->append(":" + String(SIP_PORT));
3717 }
3718 }
3719 }
3720 s_sipInfo.auths.threshold = cfg.getIntValue("sip","max_failed_auths",0);
3721 s_sipInfo.transactions.threshold = cfg.getIntValue("sip","max_transaction_timeouts",0);
3722 s_sipInfo.byes.threshold = cfg.getIntValue("sip","max_byes_timeouts",0);
3723 s_sipInfo.reset = cfg.getIntValue("sip","reset_time",0);
3724 if (s_sipInfo.reset)
3725 s_sipInfo.resetTime = Time::secNow() + s_sipInfo.reset;
3726
3727 // read SS7 monitoring
3728 bool sigEnable = cfg.getBoolValue("sig","monitor",false);
3729 m_trunkMon = cfg.getBoolValue("sig","trunk",sigEnable);
3730 m_interfaceMon = cfg.getBoolValue("sig","interface",sigEnable);
3731 m_linksetMon = cfg.getBoolValue("sig","linkset",sigEnable);
3732 m_linkMon = cfg.getBoolValue("sig","link",sigEnable);
3733 m_isdnMon = cfg.getBoolValue("sig","isdn",sigEnable);
3734
3735 // read RTP monitoring
3736 NamedList* sect = cfg.getSection("rtp");
3737 if (sect) {
3738 if (!m_rtpInfo)
3739 m_rtpInfo = new RTPTable(sect);
3740 else
3741 m_rtpInfo->reconfigure(sect);
3742 }
3743 else
3744 TelEngine::destruct(m_rtpInfo);
3745
3746 // read config for MGCP monitoring
3747 s_mgcpInfo.transactions.threshold = cfg.getIntValue("mgcp","max_transaction_timeouts",0);
3748 s_mgcpInfo.deletes.threshold = cfg.getIntValue("mgcp","max_deletes_timeouts",0);
3749 s_mgcpInfo.reset = cfg.getIntValue("mgcp","reset_time",0);
3750 s_mgcpInfo.gw_monitor = cfg.getBoolValue("mgcp","gw_monitor",false);
3751 if (s_mgcpInfo.reset)
3752 s_mgcpInfo.resetTime = Time::secNow() + s_mgcpInfo.reset;
3753
3754 }
3755
3756 // handle messages received by the module
received(Message & msg,int id)3757 bool Monitor::received(Message& msg, int id)
3758 {
3759 if (id == Halt) {
3760 DDebug(this,DebugInfo,"::received() - Halt Message");
3761 s_nodeState = "exiting";
3762 unload();
3763 }
3764 if (id == Timer) {
3765 if (m_rtpInfo && m_rtpInfo->shouldReset())
3766 m_rtpInfo->reset();
3767 if (s_sipInfo.resetTime && Time::secNow() > s_sipInfo.resetTime) {
3768 s_sipInfo.auths.counter = s_sipInfo.transactions.counter = s_sipInfo.byes.counter = 0;
3769 s_sipInfo.auths.alarm = s_sipInfo.transactions.alarm = s_sipInfo.byes.alarm = false;
3770 s_sipInfo.resetTime = Time::secNow() + s_sipInfo.reset;
3771 }
3772 if (s_mgcpInfo.resetTime && Time::secNow() > s_mgcpInfo.resetTime) {
3773 s_mgcpInfo.transactions.counter = s_mgcpInfo.deletes.counter = 0;
3774 s_mgcpInfo.transactions.alarm = s_mgcpInfo.deletes.alarm = false;
3775 s_mgcpInfo.resetTime = Time::secNow() + s_mgcpInfo.reset;
3776 }
3777 if (m_dbInfo)
3778 m_dbInfo->reset();
3779 }
3780 return Module::received(msg,id);
3781 }
3782
3783 // handle module.update messages
update(Message & msg)3784 void Monitor::update(Message& msg)
3785 {
3786 String module = msg.getValue("module","");
3787 XDebug(this,DebugAll,"Monitor::update() from module=%s",module.c_str());
3788 int type = lookup(module,s_modules,0);
3789 switch (type) {
3790 case DATABASE:
3791 if (m_dbInfo)
3792 m_dbInfo->update(msg);
3793 break;
3794 case PSTN:
3795 sendSigNotifs(msg);
3796 break;
3797 case INTERFACE:
3798 sendCardNotifs(msg);
3799 break;
3800 case RTP:
3801 if (m_rtpInfo)
3802 m_rtpInfo->update(msg);
3803 break;
3804 case SIP:
3805 case MGCP:
3806 checkNotifs(msg,type);
3807 break;
3808 default:
3809 break;
3810 }
3811 }
3812
3813 // build SS7 notifications
sendSigNotifs(Message & msg)3814 void Monitor::sendSigNotifs(Message& msg)
3815 {
3816 const String& type = msg[YSTRING("type")];
3817 const String& name = msg[YSTRING("from")];
3818 if (type.null() || name.null())
3819 return;
3820 // get the type of the notification
3821 int t = lookup(type,s_sigTypes,0);
3822 DDebug(this,DebugInfo,"Monitor::sendSigNotifs() - send notification from '%s'",name.c_str());
3823 bool up = msg.getBoolValue(YSTRING("operational"));
3824 const char* text = msg.getValue(YSTRING("text"));
3825 String notif;
3826 // build trap information
3827 switch (t) {
3828 case ISDN:
3829 if (m_isdnMon)
3830 sendTrap(lookup((up ? IsdnQ921Up : IsdnQ921Down),s_sigNotifs),name,0,text);
3831 if (!up && m_linkInfo)
3832 m_linkInfo->updateAlarmCounter(name);
3833 break;
3834 case SS7_MTP3:
3835 if (m_linksetMon) {
3836 sendTrap(lookup((up ? LinksetUp : LinksetDown),s_sigNotifs),name,0,text);
3837 if (!up && m_linksetInfo)
3838 m_linksetInfo->updateAlarmCounter(name);
3839 }
3840 notif = msg.getValue("link","");
3841 if (m_linkMon && !notif.null()) {
3842 up = msg.getBoolValue(YSTRING("linkup"),false);
3843 sendTrap(lookup(( up ? LinkUp : LinkDown),s_sigNotifs),notif);
3844 if (!up && m_linkInfo)
3845 m_linkInfo->updateAlarmCounter(name);
3846 }
3847 break;
3848 case TRUNK:
3849 if (m_trunkMon)
3850 sendTrap(lookup(( up ? TrunkUp : TrunkDown),s_sigNotifs),name,0,text);
3851 if (!up && m_trunkInfo)
3852 m_trunkInfo->updateAlarmCounter(name);
3853 break;
3854 default:
3855 break;
3856 }
3857 }
3858
3859 // build and send trap from interface notifications
sendCardNotifs(Message & msg)3860 void Monitor::sendCardNotifs(Message& msg)
3861 {
3862 String device = msg.getValue("interface","");
3863 DDebug(this,DebugInfo,"::sendCardNotifs() - a notification from interface '%s' has been received",device.c_str());
3864 if (device.null())
3865 return;
3866 String notif = msg.getValue("notify","");
3867 int type = lookup(notif,s_cardInfo,0);
3868 if (type && m_interfaceMon) {
3869 String trap = lookup(type,s_cardNotifs,"");
3870 if (!trap.null())
3871 sendTrap(notif,device);
3872 if (m_ifaceInfo)
3873 m_ifaceInfo->updateAlarmCounter(device);
3874 }
3875 }
3876
3877 // helper function to check for a value against a threshold from a BaseInfo struct and send a notification if necessary
checkInfo(unsigned int count,BaseInfo & info,unsigned int alrm,TokenDict * dict)3878 static void checkInfo(unsigned int count, BaseInfo& info, unsigned int alrm, TokenDict* dict)
3879 {
3880 DDebug(&__plugin,DebugAll,"checkInfo(count=%d, info={threshold=%d,alarm=%s,counter=%d})",
3881 count,info.threshold,String::boolText(info.alarm),info.counter);
3882 info.counter += count;
3883 if (info.threshold && !info.alarm && info.counter >= info.threshold) {
3884 info.alarm = true;
3885 String notif = lookup(alrm,dict,"");
3886 if (!notif.null())
3887 __plugin.sendTrap(notif,String(info.counter));
3888 }
3889 }
3890
3891 // handle module.update messages from SIP and MGCP
checkNotifs(Message & msg,unsigned int type)3892 void Monitor::checkNotifs(Message& msg, unsigned int type)
3893 {
3894 DDebug(&__plugin,DebugAll,"::checkNotifs() from module='%s'",lookup(type,s_modules,""));
3895 if (type == SIP) {
3896 unsigned int count = msg.getIntValue("failed_auths",0);
3897 checkInfo(count,s_sipInfo.auths,FailedAuths,s_sipNotifs);
3898
3899 count = msg.getIntValue("transaction_timeouts",0);
3900 checkInfo(count,s_sipInfo.transactions,TransactTimedOut,s_sipNotifs);
3901
3902 count = msg.getIntValue("bye_timeouts",0);
3903 checkInfo(count,s_sipInfo.byes,ByesTimedOut,s_sipNotifs);
3904 }
3905 if (type == MGCP) {
3906 unsigned int transTO = msg.getIntValue("tr_timedout",0);
3907 checkInfo(transTO,s_mgcpInfo.transactions,TransactTimedOut,s_mgcpNotifs);
3908
3909 transTO = msg.getIntValue("del_timedout",0);
3910 checkInfo(transTO,s_mgcpInfo.deletes,DeletesTimedOut,s_mgcpNotifs);
3911
3912 if (s_mgcpInfo.gw_monitor) {
3913 if (msg.getValue("mgcp_gw_down"))
3914 sendTrap(lookup(GWTimeout,s_mgcpNotifs,"mgcpGatewayTimedOut"),msg.getValue("mgcp_gw_down"));
3915 if (msg.getValue("mgcp_gw_up"))
3916 sendTrap(lookup(GWUp,s_mgcpNotifs,"mgcpGatewayUp"),msg.getValue("mgcp_gw_up"));
3917 }
3918 }
3919 }
3920
3921 // get SIP/MGCP transaction info
getTransactionsInfo(const String & query,const int who)3922 String Monitor::getTransactionsInfo(const String& query, const int who)
3923 {
3924 String result = "";
3925 if (who == SIP) {
3926 int type = lookup(query,s_sipNotifs,0);
3927 switch (type) {
3928 case TransactTimedOut:
3929 result << s_sipInfo.transactions.counter;
3930 return result;
3931 case FailedAuths:
3932 result << s_sipInfo.auths.counter;
3933 return result;
3934 case ByesTimedOut:
3935 result << s_sipInfo.byes.counter;
3936 return result;
3937 default:
3938 break;
3939 }
3940 }
3941 else if (who == MGCP) {
3942 int type = lookup(query,s_mgcpNotifs,0);
3943 switch (type) {
3944 case TransactTimedOut:
3945 result << s_mgcpInfo.transactions.counter;
3946 return result;
3947 case DeletesTimedOut:
3948 result << s_mgcpInfo.deletes.counter;
3949 return result;
3950 default:
3951 break;
3952 }
3953 }
3954 return "";
3955 }
3956
3957 // build a notification message. Increase the alarm counters if the notification was an alarm
sendTrap(const String & trap,const String & value,unsigned int index,const char * text)3958 void Monitor::sendTrap(const String& trap, const String& value, unsigned int index, const char* text)
3959 {
3960 DDebug(&__plugin,DebugAll,"Monitor::sendtrap(trap='%s',value='%s',index='%d') [%p]",trap.c_str(),value.c_str(),index,this);
3961 Message* msg = new Message("monitor.notify",0,true);
3962 if (m_newTraps)
3963 msg->addParam("notify","specificAlarm");
3964 msg->addParam("notify.0",trap);
3965 msg->addParam("value.0",value);
3966 if (text && m_newTraps) {
3967 msg->addParam("notify.1","alarmText");
3968 msg->addParam("value.1",text);
3969 }
3970 if (index)
3971 msg->addParam("index",String(index));
3972 Engine::enqueue(msg);
3973 }
3974
3975 // build a notification message. Increase the alarm counters if the notification was an alarm
sendTraps(const NamedList & traps)3976 void Monitor::sendTraps(const NamedList& traps)
3977 {
3978 Message* msg = new Message("monitor.notify",0,true);
3979 if (m_newTraps)
3980 msg->addParam("notify","specificAlarm");
3981 msg->copyParams(traps);
3982 Engine::enqueue(msg);
3983 }
3984
3985 // handle a query for a specific monitored value
solveQuery(Message & msg)3986 bool Monitor::solveQuery(Message& msg)
3987 {
3988 XDebug(__plugin.name(),DebugAll,"::solveQuery()");
3989 String query = msg.getValue("name","");
3990 if (query.null())
3991 return false;
3992 int queryWho = lookup(query,s_categories,-1);
3993 String result = "";
3994 unsigned int index = msg.getIntValue("index",0);
3995 DDebug(__plugin.name(),DebugAll,"::solveQuery(query=%s, index=%u)",query.c_str(),index);
3996 switch (queryWho) {
3997 case DATABASE:
3998 if (m_dbInfo)
3999 result = m_dbInfo->getInfo(query,index,s_databaseQuery);
4000 break;
4001 case CALL_MONITOR:
4002 if (m_callMonitor)
4003 m_callMonitor->get(query,index,result);
4004 break;
4005 case ACTIVE_CALLS:
4006 if (m_activeCallsCache)
4007 result = m_activeCallsCache->getInfo(query,index,s_activeCallInfo);
4008 break;
4009 case TRUNKS:
4010 if (m_trunkInfo)
4011 result = m_trunkInfo->getInfo(query,index,m_trunkInfo->s_trunkInfo);
4012 break;
4013 case LINKSETS:
4014 if (m_linksetInfo)
4015 result = m_linksetInfo->getInfo(query,index,m_linksetInfo->s_linksetInfo);
4016 break;
4017 case LINKS:
4018 if (m_linkInfo)
4019 result = m_linkInfo->getInfo(query,index,m_linkInfo->s_linkInfo);
4020 break;
4021 case IFACES:
4022 if (m_ifaceInfo)
4023 result = m_ifaceInfo->getInfo(query,index,m_ifaceInfo->s_ifacesInfo);
4024 break;
4025 case ACCOUNTS:
4026 if (m_accountsInfo)
4027 result = m_accountsInfo->getInfo(query,index,s_accountInfo);
4028 break;
4029 case ENGINE:
4030 if (m_engineInfo)
4031 result = m_engineInfo->getInfo(query,index,s_engineQuery);
4032 break;
4033 case MODULE:
4034 if (m_moduleInfo)
4035 result = m_moduleInfo->getInfo(query,index,s_moduleQuery);
4036 break;
4037 case AUTH_REQUESTS:
4038 if (m_authHandler)
4039 result = m_authHandler->getCount();
4040 break;
4041 case REGISTER_REQUESTS:
4042 if (m_registerHandler)
4043 result = m_registerHandler->getCount();
4044 break;
4045 case RTP:
4046 if (m_rtpInfo)
4047 result = m_rtpInfo->getInfo(query,index);
4048 break;
4049 case SIP:
4050 case MGCP:
4051 result = getTransactionsInfo(query,queryWho);
4052 break;
4053 default:
4054 return false;
4055 }
4056 msg.setParam("value",result);
4057 return true;
4058 }
4059
4060 // verify if a call hasn't hangup because of a gateway timeout. In that case, if the gateway was
4061 // monitored send a notification
handleChanHangup(const String & address,int & code)4062 void Monitor::handleChanHangup(const String& address, int& code)
4063 {
4064 DDebug(this,DebugInfo,"::handleChanHangup('%s', '%d')",address.c_str(),code);
4065 if (address.null())
4066 return;
4067 if (m_sipMonitoredGws && m_sipMonitoredGws->find(address)) {
4068 if (code == 408 && !m_timedOutGws.find(address)) {
4069 sendTrap(lookup(GWTimeout,s_sipNotifs,"gatewayTimeout"),address);
4070 m_timedOutGws.append(new String(address));
4071 }
4072 }
4073 }
4074
4075 // if a call has passed through, get the gateway and verify if it was previously down
4076 // if it was send a notification that the gateway is up again
verifyGateway(const String & address)4077 bool Monitor::verifyGateway(const String& address)
4078 {
4079 if (address.null())
4080 return false;
4081 if (m_timedOutGws.find(address)) {
4082 m_timedOutGws.remove(address);
4083 sendTrap(lookup(GWUp,s_sipNotifs,"gatewayUp"),address);
4084 }
4085 return true;
4086 }
4087
4088 };
4089
4090 /* vi: set ts=8 sw=4 sts=4 noet: */
4091