-
Notifications
You must be signed in to change notification settings - Fork 3
/
asterisk_stat.sh
339 lines (300 loc) · 11.4 KB
/
asterisk_stat.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#!/bin/bash
#
# ------------------------------------------------------------------------------
# REBTEL/SIPInfra Post metric to stackdriver GCP (BETA)
# ------------------------------------------------------------------------------
# This script is only intended as a cron job Scripts to post metrics into
# stackdriver GCP.
# It will run on all our media server instances to perform channels stats query
# using asterisk cli interface every 1 minute.
#
# Don't forget to give your instance gcp api access
# "Cloud API access scopes -> Stackdriver Monitoring API".
# ------------------------------------------------------------------------------
#
# (c) 2018 Rebtel <[email protected]>
#
#####################################################################
[[ "$TRACE" ]] && { set -x; set -o functrace; }
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
my_pid=$$
# SIPInfra parameters
gcp_project_id="rebtel-calling-infra-prod"
gcp_instance_id="prod-custom-image-eu-west3-a-v1"
gcp_zone="europe-west3-a"
#### NO CHANGES BELOW THIS LINE!
VERSION=0.0.2
MRC=0
# Lock the Scripts
[[ "$LOCKFILE" == "" ]] && LOCKFILE="/var/lock/`basename $0`"
LOCKFD=99
# PRIVATE
_lock() { flock -$1 $LOCKFD; }
_no_more_locking() { _lock u; _lock xn && rm -f $LOCKFILE; }
_prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }
# ON START
_prepare_locking
# PUBLIC
exlock_now() { _lock xn; } # obtain an exclusive lock immediately or fail
exlock() { _lock x; } # obtain an exclusive lock
shlock() { _lock s; } # obtain a shared lock
unlock() { _lock u; } # drop a lock
# Simplest example is avoiding running multiple instances of script.
exlock_now || {
echo "ERROR: `basename $0` already running!"
exit 1
}
######################################################################
#
# Start of function definitions
#
######################################################################
is_root_user() {
# Function to check that the effective user id of the user running
# the script is indeed that of the root user (0)
if [[ $EUID != 0 ]]; then
return 1
fi
return 0
}
locate_cmd() {
# Function to return the full path to the cammnd passed to us
# Make sure it exists on the system first or else this exits
# the script execution
local cmd="$1"
local valid_cmd=""
# valid_cmd=$(hash -t $cmd 2>/dev/null)
valid_cmd=$(command -v $cmd 2>/dev/null)
if [[ ! -z "$valid_cmd" ]]; then
echo "$valid_cmd"
else
echo "HALT: Please install package for command '$cmd'"
/bin/kill -s TERM $my_pid
fi
return 0
}
check_status() {
# Function to check and do something with the return code of some command
local return_code="$1"
if [[ $return_code != 0 ]]; then
echo "HALT: Return code of command was '$return_code', aborting."
echo "Please check the log above and correct the issue."
exit 1
fi
}
prepare_request() {
# Function to prepare the request body of the curl request to gcp api
local time_series_type=$1
local time_series_value=$2
local metric_type="custom.googleapis.com/"
local cmd_date=$(locate_cmd "date")
local time_series_endTime=$($cmd_date --utc +%FT%T.%3NZ)
case "$time_series_type" in
"active_calls" ) metric_type="${metric_type}active_calls" ;;
"active_channels" ) metric_type="${metric_type}active_channels" ;;
"calls_processed" ) metric_type="${metric_type}calls_processed" ;;
* ) metric_type="" ;; # Unsupported call
esac
if [[ ! -z "$metric_type" ]]; then
local request_body='{
"timeSeries": [
{
"metric": {
"type": "'$metric_type'"
},
"resource": {
"type": "gce_instance",
"labels": {
"instance_id": "'$gcp_instance_id'",
"zone": "'$gcp_zone'",
"project_id": "'$gcp_project_id'"
}
},
"points": [
{
"interval": {
"endTime": "'$time_series_endTime'"
},
"value": {
"doubleValue": '$time_series_value'
}
}
]
}
]
}'
echo "$request_body"
else
echo "HALT: GCP metric type '$metric_type' error !" >&2
/bin/kill -s TERM $my_pid
exit 1
fi
return 0
}
get_token(){
# Function to get the authentification token for the gcp api
# https://developers.google.com/identity/protocols/OAuth2InstalledApp
local cmd_curl=$(locate_cmd "curl")
local response_body=$($cmd_curl -s "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" -H "Metadata-Flavor: Google")
local response_regexp="^.*\"access_token\":\"(.*)\".*\"expires_in\":([0-9]+).*$"
if [[ $response_body =~ $response_regexp ]]; then
local access_token=${BASH_REMATCH[1]}
local expires_in=${BASH_REMATCH[2]}
#if [[ "$expires_in" -ge "30" ]]; then
#if [[ "$expires_in" -le "30" ]]; then
if (( $expires_in > 15 )); then
echo $access_token
else
local cmd_sleep=$(locate_cmd "sleep")
MRC=$((MRC+1))
$cmd_sleep $((expires_in+1))
if (( $MRC < 2 )); then
echo $(get_token)
else
echo "HALT: Max recursive calls reached $MRC ! GCP Request error '$response_body'" >&2
/bin/kill -s TERM $my_pid
exit 1
fi
fi
else
echo "HALT: GCP Request error '$response_body'" >&2
/bin/kill -s TERM $my_pid
exit 1
fi
}
execute_request() {
# Function to execute the google API POST request
# https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.timeSeries/create
# POST https://monitoring.googleapis.com/v3/{name}/timeSeries
local time_series_type=$1
local time_series_value=$2
local request_body=""
case "$time_series_type" in
"active_calls" ) request_body=$(prepare_request "active_calls" $time_series_value) ;;
"active_channels" ) request_body=$(prepare_request "active_channels" $time_series_value) ;;
"calls_processed" ) request_body=$(prepare_request "calls_processed" $time_series_value) ;;
* ) request_body="" ;; # Unsupported call
esac
if [[ ! -z "$request_body" ]]; then
local cmd_curl=$(locate_cmd "curl")
local api_url="https://monitoring.googleapis.com/v3/projects/$gcp_project_id/timeSeries"
local api_token=$(get_token)
$cmd_curl -s --data "$request_body" $api_url -H "Authorization":"Bearer $api_token" -H "Accept: application/json" -H "Content-Type:application/json"
check_status "$?"
else
echo "HALT: GCP metadata '$metric_type' request error !" >&2
/bin/kill -s TERM $my_pid
exit 1
fi
return 0
}
asterisk_stat() {
# This is the main app, get asterisk stat from cli
local cmd_rm=$(locate_cmd "rm")
$cmd_rm -rf "/usr/share/asterisk/static-http/current-status.html"
local cmd_date=$(locate_cmd "date")
local start_date=$($cmd_date '+%Y-%m-%d %H:%M:%S')
echo 2>&1
echo "######################################################################"
echo "[$start_date] - Rebtel Stack Driver Stat: Start execution..."
echo "######################################################################"
echo 2>&1
if ! is_root_user; then
echo "ERROR: You must be the root user. Exiting..." 2>&1
echo 2>&1
exit 1
fi
local cmd_asterisk=$(locate_cmd "asterisk")
local cmd_tail=$(locate_cmd "tail")
local ast_data=$($cmd_asterisk -rx 'core show channels' | $cmd_tail -3)
local ast_pjsip=$($cmd_asterisk -rx 'pjsip show version')
local regexp_channels="^([0-9]+) active channel.{0,1}$"
local regexp_calls="^([0-9]+) active call.{0,1}$"
local regexp_processed="^([0-9]+) call.{0,1} processed$"
local regexp_pjsip="^PJPROJECT version currently running against: ([0-9]+\.?[0-9]+\.?[0-9]+)$"
if [[ $ast_pjsip =~ $regexp_pjsip ]] ; then
local pjsip_version=${BASH_REMATCH[1]}
else
echo "ERROR: pjsip probably not loaded ! Exiting..." 2>&1
echo 2>&1
exit 1
fi
local cmd_wc=$(locate_cmd "wc")
local ast_data_nbl=$($cmd_wc -l <<< "$ast_data")
if [[ "$ast_data_nbl" != "3" ]] ; then
echo "ERROR: asterisk channels stat wrong output ! Exiting..." 2>&1
echo 2>&1
exit 1
fi
if [[ !(-z "$ast_data") && !(-z $ast_pjsip) ]]; then
echo '<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Asterisk Status Page</title>
<meta name="viewport" content="width=device-width">
<meta name="robots" content="noindex, nofollow">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<div class="container" style="width: 40%; margin:0 auto;">
<div class="page-header">
<h1>
Asterisk Status Page
</h1>
</div>
<ul class="list-group">
<li class="list-group-item list-group-item-success">PJSIP version currently running '$pjsip_version'</li>
' > /usr/share/asterisk/static-http/current-status.html 2> /dev/null
while read -r line; do
if [[ $line =~ $regexp_channels ]] ; then
execute_request "active_channels" ${BASH_REMATCH[1]} >/dev/null 2>&1
echo '<li class="list-group-item">
<span class="badge"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> '${BASH_REMATCH[1]}'</span>
active channels</li>' >> /usr/share/asterisk/static-http/current-status.html 2> /dev/null
else
if [[ $line =~ $regexp_calls ]] ; then
execute_request "active_calls" ${BASH_REMATCH[1]} >/dev/null 2>&1
echo '<li class="list-group-item">
<span class="badge"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> '${BASH_REMATCH[1]}'</span>
active calls</li>' >> /usr/share/asterisk/static-http/current-status.html 2> /dev/null
else
if [[ $line =~ $regexp_processed ]] ; then
execute_request "calls_processed" ${BASH_REMATCH[1]} >/dev/null 2>&1
echo '<li class="list-group-item">
<span class="badge"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> '${BASH_REMATCH[1]}'</span>
calls processed</li>' >> /usr/share/asterisk/static-http/current-status.html 2> /dev/null
fi
fi
fi
done <<< "$ast_data"
echo '</ul>
<hr>
<footer style="position: absolute;right: 0;bottom: 0;left: 0;padding: 1rem;background-color: #efefef;text-align: center;">
<p><a href="https://www.rebtel.com/en/">Rebtel SIP INFRA</a> © 2018
<span class="text-muted"> - Generated at '$start_date'</span></p>
</footer>
</div>
</body>
</html>' >> /usr/share/asterisk/static-http/current-status.html 2> /dev/null
else
echo "HALT: asterisk execute core show channels error !" >&2
/bin/kill -s TERM $my_pid
exit 1
fi
local time_series=$($cmd_date --utc +%FT%T.%3NZ)
echo "$time_series: Asterisk stats exported successfully, ENJOY :)"
exit 0
}
######################################################################
#
# End of function definitions
#
######################################################################
######################################################################
#
# Start of main script
#
######################################################################
# only allow root to run the script
[[ "$0" == "$BASH_SOURCE" ]] && asterisk_stat