AsteriskでODBCを利用してDB連携しよう

こんにちは。
Asteriskを触りはじめてから2年くらいのエンジニアです。
ダイアルプランからCDR(call detail records)以外にも、カスタム情報をDBに保存したい時があると思います。
 
やり方としてはAGIを使えばいろいろな言語で実装できますが、今回はAsteriskに実装されているODBCファンクションを利用したいと思います。
注意点として、AsteriskのMYSQLファンクションは最新バージョンではサポートされていないので使わないほうが良いです。

目次

前提条件

以下の構成で作業を行います。
 

  • Asterisk 13.12.1
  • mmysql Ver 14.14 Distrib 5.1.73
  •  
    Asteriskはメジャーバージョンの違いがなければ問題ないと思います。
    mysqlはバージョンの差異による影響はほとんどなさそうです。
    Asterisk, mysql,に加えて、ODBCのパッケージ諸々が必要です。
    以下のコマンドを実行して追加します。
     

    # sudo yum install unixODBC unixODBC-devel libtool-ltdl libtool-ltdl-devel
    
    # sudo yum install mysql-connector-odbc

    ODBCドライバを設定する

    新しいバージョンのUnixODBCをインストールしていれば、以下の内容に近い、/etc/odbcinst.iniが自動で作成されています。
     

    # Driver from the mysql-connector-odbc package
    # Setup from the unixODBC package
    [MySQL]
    Description     = ODBC for MySQL
    Driver          = /usr/lib/libmyodbc5.so
    Setup           = /usr/lib/libodbcmyS.so
    Driver64        = /usr/lib64/libmyodbc5.so
    Setup64         = /usr/lib64/libodbcmyS.so
    FileUsage       = 1
    Pooling         = Yes
    CPTimeout       = 120

     
    このファイルは特に変更する必要はありません。

    Mysql ODBCコネクタの設定をする

    ODBCがmysqlに接続をする具体的な設定を行います。
    以下にならって設定をします。
     

    [asterisk-connector]
    #説明
    Description=MySQL connection to 'asterisk' database
    #上記で作成したドライバ名を使用する
    driver=MySQL
    #mysqlのホスト
    server=localhost
    #database名
    database=asterisk
    #mysqlのポート
    Port=3306
    #mysqlのソケット
    Socket=/var/lib/mysql/mysql.sock
    #charset
    Charset=utf8

     
    ここで重要なポイントは、driverにodbcinst.iniで設定したドライバ名を指定することです。
    それ以外は環境に合わせて入力してください。

    AsteriskのODBC接続設定

    上記で作成したODBCコネクタをAsterisk上で利用する設定をします。
    設定ファイルは、/etc/asterisk/res_odbc.confです。
    freepbxの場合、/etc/asterisk/res_odbc_custom.confです。
     

    [asteriskodbc]
    #yesで有効に
    enabled=>yes
    #ODBCコネクタ名を指定
    dsn=>asterisk-connector
    #DB接続ユーザー
    username=>user
    #DB接続パスワード
    password=>password
    #接続先DB名
    database=>asterisk

     
    ここで重要なポイントは、dsnに上記で設定したODBCコネクタ名を指定することです。
    enabledはyesに設定して、その他は環境に合わせて設定してください。

    ODBCファンクションの設定

    ダイアルプランから呼び出し、DBの読み書きを行う関数を定義します。
    設定ファイルは、/etc/asterisk/func_odbc.confです。
     

    #関数名
    [INSERT_RECORD]
    #dsn名を指定
    dsn=asteriskodbc
    #書き込みを行うSQL
    writesql=INSERT INTO uui(uui) VALUES('${SQL_ESC(${VAL1})}')
    
    #関数名
    [SELECT_RECORD]
    #dsn名を指定
    dsn=asteriskodbc
    #書き込みを行うSQL
    readsql=SELECT column FROM table WHERE id=${ARG1}
    

     
    上記で書き込み、読み込みの各関数を定義しました。
    func_odbc.confで設定するdsn名は、AsteriskのODBC接続設定で設定したものを利用します。

    ダイアルプランからODBCファンクションを呼び出す

    下記のダイアルプランでODBCファンクションを呼び出します。
     

    [odbctest]
    exten => s,1,Answer()
    same => n,NoOp(ODBC test dayo)
    same => n,Set(ODBC_INSERT_RECORD()=${FROMEXTEN})
    same => n,Set(RET=${ODBC_SELECT_RECORD(1)})
    same => n,NoOp(select result is ${RET})
    
    結果↓
    
    -- Executing [s@odbctest:1] Answer("SIP/1234o-00000024", "") in new stack
    -- Executing [s@odbctest:2] NoOp("SIP/1234o-00000024", "ODBC test dayo") in new stack
    -- Executing [s@odbctest:3] Set("SIP/1234o-00000022", "ODBC_RECORD_UUI()=0338636754") in new stack
    -- Executing [s@odbctest:4] Set("SIP/1234o-00000022", "RET=test") in new stack
    -- Executing [s@odbctest:5] NoOp("SIP/1234o-00000022", "select result is test") in new stack
    

     
    呼び出すときは、
    ODBC_関数名
    で呼び出すことができます。

    トラブルシューティング

    作成したODBCファンクションが呼び出されない時

    具体的には、以下のエラーが出たときです。
     

    ERROR[6984][C-00000020]: pbx_functions.c:608 ast_func_read: Function ODBC_SELECT_RECORD not registered
    

     
    この場合、作成したファンクションがodbcモジュールで認識されていないので、
    以下のコマンドでモジュールをリロードする必要があります。
     

    # asterisk -rvvvvv
    $ module reload func_odbc.so 
    

     

    AsteriskのODBC接続設定を確認したい時

    以下コマンドで読み込まれているODBC接続設定を確認することができます。
     

    # asterisk -rvvvv
    $ odbc show
    
    ODBC DSN Settings
    -----------------
    
      Name:   asteriskodbc
      DSN:    asterisk-connector
        Last connection attempt: 1970-01-01 09:00:00
        Number of active connections: 1 (out of 1)
    

     
    また、
    odbc read
    もしくは
    odbc write
    コマンドで読み込まれている定義された関数を確認することができます。
     

    # asterisk -rvvvv
    $ odbc 
    read   show   write  
    $ odbc read 
    ODBC_INSERT_RECORD  ODBC_SELECT_RECORD  #定義されている関数の一覧
    $ odbc read ODBC_SELECT_RECORD 
    Usage: odbc read <name> <args> [exec]
           Evaluates the SQL provided in the ODBC function <name>, and
           optionally executes the function.  This function is intended for
           testing purposes.  Remember to quote arguments containing spaces.

    おまけ:一度に複数のカラムを更新する

    ODBCファンクションの構文は、以下のようになっています。
     

    ODBC_functionname([...[,]])[=[val1][...[,valN]]
    

     
    わかりづらいのは、関数の引数として与えたargNも、右オペランドで与えたval1も関数内で利用できる変数だということです。
     

    # func_odbc.conf
    [INSERT_RECORD]
    dsn=asteriskodbc
    writesql=INSERT INTO uui(uui, description) VALUES('${SQL_ESC(${VAL1})}', '${SQL_ESC(${VAL2})}')
    
    ### 関数を変更した場合は以下を実行 ###
    # asterisk -rvvvv
    $ module load func_odbc.so
    
    # dialplan
    # 右オペランドにカンマ区切りでVALを入れる
    same => n,set(ODBC_RECORD_UUI()=${FROMEXTEN},"my number")
    

     
    以下のようにARGとVALをミックスして使用することもできます。
     

    [INSERT_RECORD]
    dsn=asteriskodbc
    writesql=INSERT INTO uui(uui, description) VALUES('${SQL_ESC(${VAL1})}', '${SQL_ESC(${ARG1})}')
    
    ## Linuxコマンドラインから一発でモジュールリロード
    # asterisk -rx "module load func_odbc.so"
    
    # dialplan
    same => n,set(ODBC_RECORD_UUI("mix example")=${FROMEXTEN})
    
    ### ARGにスペースが含まれる引数を与えると、以下のようなWARNINGが出ます。
    
    [2019-07-24 08:40:42] WARNING[26989][C-00000033]: pbx_variables.c:1101 pbx_builtin_setvar: Please avoid unnecessary spaces on variables as it may lead to unexpected results ('ODBC_INSERT_RECORD("mix example")' set to '0338636754').
    
    # 推奨されないようですが、一応Mysqlにはスペースを含むデータが入っていました
    

    おわりに

    これまではAGIからPHPのPDOを通してDB連携を行うことが多かったですが、
    ODBCを利用することでよりダイアルプランを読んだだけでDB処理がわかるようになりました。
     
    数少ない(?)Asteriskエンジニアの一人としてAsteriskの情報は今後も発信していきたいと思います。

    この記事が気に入ったら
    いいね ! しよう

    Twitter で

    【採用情報】一緒に働く仲間を募集しています

    採用情報
    ページトップへ