Skip to content

Instantly share code, notes, and snippets.

@andrius
Created February 27, 2013 22:16
Show Gist options
  • Save andrius/5052336 to your computer and use it in GitHub Desktop.
Save andrius/5052336 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'monitor'
require 'pp'
require 'timeout'
require 'time'
require 'pp'
require 'rubygems'
require 'AGIServer'
# this script work with installed to /usr/sbin astcli app i.e. from
# /usr/src/asterisk/asterisk-1.6.2.6/contrib/scripts/astcli
# and enabled asterisk manager user with permission to execute commands and originate
# in /etc/asterisk/manager.conf, user must be allowed for 127.0.0.1
#
# in extensions_custom put:
# [check-system-status]
# exten => request,1,NoCDR
# exten => request,n,Answer
# exten => request,n,Wait(180)
#
# If call are unsuccesful or astcli cannot connect to asterisk, it will be restarted via
# /etc/init.d/asterisk command. 20 seconds later command 'asterisk' will be executed under root user,
# so if /etc/init.d/asterisk not-exists or failed, there are other chance to start system.
#
# Since asterisk processes may be started as different users and from different folters,
# such processes will be killed during restart (can be modified in kill_processes command):
$in_debug_mode = true
# timeout in seconds for AGI (for debug should be higher value)
TIMEOUT = 5
# check interval (in seconds)
CHECK_INTERVAL = 120
# service we monitor and restart
SERVICE = 'asterisk'
SEND_EMAIL_TO = '[email protected]'
$call_status = :never_received
# tiny FastAGI class, answer the call and setup global variable
class Call < AGIRoute
def register
begin
agi.answer
agi.exec "NoOp \"Call received, recorded, thanks!\""
$call_status = :answered
agi.exec "Busy 8"
rescue => err
pp err.backtrace
end
end
end
class CallServer
class << self
def start
puts "#{Time.now} call tester started"
begin
unless defined?(@@server)
@@logger = Logger.new(STDERR)
@@logger.level = $in_debug_mode ? Logger::DEBUG : Logger::FATAL
@@server = AGIServer.new :bind_port => 30303, :bind_host => '127.0.0.1',
:min_workers => 2, :max_workers => 2, :jobs_per_worker => 2,
:logger => @@logger, :stats => false
@@server.start
end
rescue => err
pp err.backtrace
end
end
def stop
begin
sleep 10
@@server.shutdown if defined?(@@server)
puts "#{Time.now} call tester stopped"
rescue => err
pp err.backtrace
end
end
end
end
class PhoneSystem
class << self
def verbose?
ARGV[0] == 'silent' ? false : true
end
def send_by_email?
ARGV[0] == 'sendemail' ? true : false
end
def words_to_seconds(args)
args[:days].to_i * 86400 + args[:hours].to_i * 3600 + args[:minutes].to_i * 60 + args[:seconds].to_i
end
def system_name
@@system_name ||= File.read('/etc/asterisk/asterisk.conf').split(/\n/).delete_if { |item| !item.match(/^systemname/i) }[-1].split(/=|;/)[1].strip rescue ""
@@system_name
end
def hostname
@@hostname ||= `hostname`.chomp.strip
@@hostname
end
def get_asterisk_manager_credentials(params = {:filename => '/etc/asterisk/manager.conf'})
manager_data = `sudo cat #{params[:filename]} | grep -v '\\[general' | grep '\\[' -A 4`
if manager_data =~ /\[(.+)\][^secret]*secret\s*=\s*(.*)/ then
[$1, $2]
else
[nil, nil]
end
end
def db_access
$db_access_data ||= {}
if $db_access_data.empty? then
`cat /etc/asterisk/res_mysql.conf | grep '=' | grep -v '^;'`.split("\n").each do |rec|
k,v = rec.split('=')
$db_access_data[k.strip.to_sym] = v.strip
end
end
$db_access_data
end
def astcli
unless defined?(@@manager_username)
@@manager_username, @@manager_secret = get_asterisk_manager_credentials
end
"sudo astcli -u #{@@manager_username} -s #{@@manager_secret}"
end
def kill_processes(process_name)
filter = `ps axw | grep #{process_name} | grep -v 'grep\\|restart_\\|call\\|log\\|etc' | awk '{print $1}' | xargs`
if $in_debug_mode
pp `ps axw | grep #{process_name} | grep -v 'grep\\|restart_\\|call\\|log\\|etc'`
else
processes = filter.strip.chomp.strip
`sudo kill -9 #{processes} 2>&1 > /dev/null` unless processes == ""
end
end
def stop_asterisk
kill_processes 'safe_asterisk'
kill_processes 'asterisk'
kill_processes 'mpg123'
sql = "update sip_accounts set regserver = null, useragent = null, regseconds = 0, port = 0, ipaddr = null, fullcontact = '' where regserver = '#{system_name}'"
if $in_debug_mode then
puts sql
else
sleep 3
`sudo /etc/init.d/asterisk stop`
`sudo mysql -h\"#{db_access[:dbhost]}\" -u\"#{db_access[:dbuser]}\" -p\"#{db_access[:dbpass]}\" -D\"#{db_access[:dbname]}\" -e\"#{sql}\"`
sleep 3
end
end
def start_asterisk
unless $in_debug_mode
`sudo /etc/init.d/asterisk start`
sleep 20
`sudo asterisk`
end
end
def restart_asterisk
stop_asterisk
start_asterisk
end
def sendmail(params)
server_status = params[:status] || "unreacheable"
message = params[:message]
`echo "#{'DEBUG MODE!!! ' if $in_debug_mode}#{message}" | mail -s "[ERR] Asterisk PBX #{system_name} is #{server_status}" #{SEND_EMAIL_TO}`
end
def execute_asterisk_command(command)
result, exitstatus = Timeout.timeout(5) do
begin
[`#{astcli} '#{command}' 2>&1`, $?.exitstatus.to_i]
rescue Exception
['Request timeout', 1]
rescue Timeout::Error
['Request timeout', 1]
rescue
['Request timeout', 1]
end
end
if result =~ /Authentication failed|Request timeout|Could not connect to Host:/ then
restart_asterisk
sendmail :status => :unreacheable, :message => "Asterisk manager problem: #{result.chomp.strip}. Asterisk are restarted"
# exit # cannot exit in daemons! wait instead
sleep 5
end
result.chomp.strip
end
def wait_for_call(params)
start_time = Time.now
loop do
puts "Waiting for callback result, current is #{$call_status}" if $in_debug_mode
break :timeout if ( Time.now - start_time ) > params[:timeout]
break $call_status unless $call_status == :never_received
sleep 2
end
end
def send_three_calls
1.upto(3) do |attempt_no|
break :call_status if $call_status == :answered
puts "#{Time.now} sending call attempt # #{attempt_no}, current call status is #{$call_status}."
execute_asterisk_command "originate Local/request@check-system-status application AGI 'agi://localhost:30303/Call/register'"
#execute_asterisk_command "originate Local/request@check-system-status extension register@check-system-status"
result = wait_for_call :timeout => TIMEOUT
puts "#{Time.now} call status after test is #{$call_status} / #{result} (attempt #: #{attempt_no})"
break :call_status if $call_status == :answered
end
end
def check_status
puts "checking status" if $in_debug_mode
send_three_calls
unless $call_status == :answered then
stop_asterisk
start_asterisk
sendmail :status => :frozen, :message => "Asterisk does not accept calls, restarted it"
end
end
end
end
def test
puts PhoneSystem::system_name
puts PhoneSystem::hostname
pp PhoneSystem::get_asterisk_manager_credentials
pp PhoneSystem::db_access
puts PhoneSystem::execute_asterisk_command "core show uptime"
CallServer::start
puts PhoneSystem::check_status
puts $call_status
CallServer::stop
exit
end
def run_it
CallServer::start
trap("INT") { CallServer::stop }
trap("TRAP") { CallServer::stop }
PhoneSystem::check_status
CallServer::stop
end
if $in_debug_mode then
test
else
run_it
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment