AWS LambdaからのPython&CData ODBC Driverによるkintoneデータ操作

#はじめに
##概要
CData ODBC Driverを用いて、AWSのサーバレスサービスLambdaからPythonコードを呼び出しkintoneアプリ内のデータを取り扱う手順です。

##構成図
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/f13493b7-a075-1390-50d3-53a1babab4d6.png)

##使用製品・サービス
・[AWS Lambda](https://aws.amazon.com/jp/lambda/)
・[kintone](https://kintone.cybozu.com/jp/)
・[CData kintone ODBC Driver for Linux](http://www.cdata.com/jp/drivers/kintone/odbc/)
　※2017/08/20現在、kinton向けのβ版は[こちら](https://drive.google.com/drive/folders/0BzvIxzhs1P48NlNDU0RCbGlBMTA?usp=sharing)からダウンロード可能
　　その他のデータソースのODBCドライバは、[こちら](http://www.cdata.com/download/?f=odbc)からダウンロード可能
・pyodbc
・RHEL5.7 (pyodbcライブラリ抽出用)

##サンプルコード
https://github.com/kuwazzy/pycdatakintonedemo
##参考
・[CData kintone ODBC Driver製品マニュアル - Pythonからの利用](http://cdn.cdata.com/help/LKB/odbc/pg_usageinpython.htm)
・[stack overflow - LambdaからpyODBCの使用](https://stackoverflow.com/questions/44151272/cannot-find-odbc-driver-in-aws-lambda-when-using-unixodbc)

#Lambda関数パッケージの準備
##CData kintone ODBC Driverの準備
CData SoftwareのWebページにアクセスします。CData kintone ODBC Driverの評価版、および、製品版は[こちら](http://www.cdata.com/jp/drivers/kintone/odbc/)からダウンロード可能です。ただし、2017/08/20現在、Linux版はβ版での提供のため[こちら](https://drive.google.com/drive/folders/0BzvIxzhs1P48NlNDU0RCbGlBMTA?usp=sharing)からダウンロード可能してください。製品マニュアルは[こちら](http://cdn.cdata.com/help/LKB/odbc/default.htm)をご覧ください。
##CData kintone ODBC Driver for Linux版ビルドからライブラリの抽出
ビルドファイルsetup.x86_64.debを解凍します。解凍するとdata.tarが作成されるので更に解凍します。すると以下の構造のdataディレクトリが作成されます。

data/
├ opt/
｜　└ cdata/
｜　　　└ cdata-odbc-driver-for-kintone/
｜　　　　　├ bin/
｜　　　　　├ db/
｜　　　　　├ demos/
｜　　　　　├ etc/
｜　　　　　├ help/
｜　　　　　└ lib/
｜　　　　　　　├ cdata.odbc.kintone.ini
｜　　　　　　　├ CData.ODBCm.Kintone.DLL
｜　　　　　　　├ libcdatart.x64.so.4
｜　　　　　　　└ libkintoneodbc.x64.so
└ usr/
　　└ doc/
　　　　└ cdata-odbc-driver-for-kintone/


/data/opt/cdata/cdata-odbc-driver-for-kintone/lib 配下の4ファイルを取り出します。

##pyodbc、および、依存ライブラリの準備

pyodbc.so ライブラリを準備します。私の環境では、RHEL7.3の/usr/lib64/python2.7/site-packages配下からコピーしました。

また、あわせてpyodbcの依存ライブラリ群を準備します。私の環境では、RHEL7.3の/usr/lib64配下からコピーしました。

追加した依存ライブラリ一覧

libodbc.so
libodbc.so.2
libodbccr.so
libodbccr.so.2
libodbcdrvcfg1S.so
libodbcdrvcfg1S.so.2
libodbcdrvcfg2S.so
libodbcdrvcfg2S.so.2
libodbcinst.so
libodbcinst.so.2
libodbcminiS.so
libodbcminiS.so.2
libodbcmyS.so
libodbcmyS.so.2
libodbcnnS.so
libodbcnnS.so.2
libodbcpsqlS.so
libodbcpsqlS.so.2
libodbctxtS.so
libodbctxtS.so.2
libomapi.so.0
liboplodbcS.so
liboplodbcS.so.2
liboraodbcS.so
liboraodbcS.so.2

##Pythonコードの準備
[こちら](https://github.com/kuwazzy/pycdatakintonedemo)のGitHubから取得してください。

```pycdatakintonedemo.py

# -*- coding: utf-8 -*-
import pyodbc
import sys

def Main(event, context):
    print('************************************************')
    print('\t\t Kintone Demo')
    print('This demo uses the CData ODBC for Kintone')
    print('************************************************')
    print('option:1,               - List all the tables in the database')
    print('option:2, table:name    - List all the columns for a specific table')
    print('option:3, table:name    - Select data from table')
    print('option:4, sql:statement - Custom SQL Query')
    print('------------------------------------------------')

    connStr =  'Driver={./cdata/libkintoneodbc.x64.so};' + event['conn_str']
    conn = pyodbc.connect(connStr)

    if event['option'] == '1':
        for table in conn.cursor().tables():
            print(table.table_name)
    elif event['option'] == '2':
        tableName = event['table']
        for column in conn.cursor().columns(tableName):
            print(column.column_name)
    elif event['option'] == '3':
        tableName = event['table']
        c = conn.cursor();
        c.execute('SELECT * FROM ' + tableName)
        for row in c.fetchall():
            print(row)
    elif event['option'] == '4':
        sql = event['sql']
        c = conn.cursor();
        c.execute(sql)
        for row in c.fetchall():
            print(row)
    else:
            print('Invalid option')

    conn.close();

    return {
        'status' : 'finish'
    }
```

注意点は、ODBCのライブラリをDSNでなく直指定している点です。[こちら](https://stackoverflow.com/questions/44151272/cannot-find-odbc-driver-in-aws-lambda-when-using-unixodbc)のページを参考にしました。

```
 connStr =  'Driver={./cdata/libkintoneodbc.x64.so};' + event['conn_str']
```

##関数パッケージの作成
上記手順で準備したファイル群を下記のディレクトリ構造で準備します。
.
├ pycdatakintonedemo.py　　(Lambdaから呼び出されるPythonコード)
├ pyodbc.so　　(pyodbcライブラリ)
├ cdata/　　　　(CDataライブラリ)
｜　├ cdata.odbc.kintone.ini
｜　├ CData.ODBCm.Kintone.DLL
｜　├ libcdatart.x64.so　(libcdatart.x64.so.4をリネーム)
｜　└ libkintoneodbc.x64.so
└ lib/
　　└ libodbc*.so etc　(pyodbc依存ライブラリ群)

本ファイル群をzip化します。※親ディレクトリは含めないでください

#Lambda関数の作成
##関数の作成
AWSLambdaのダッシュボード画面にて「関数の作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/f051c0f3-1d70-d37a-9b0e-1349697d9710.png)
ステップ１の設計図の選択画面にて「一から作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/671713dc-f659-fce5-8457-a2406d4a2d8c.png)
ステップ2のトリガーの設定画面では今回は設定しないので「次へ」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/40f12a3b-0a8a-4649-715e-17dc6e57248e.png)
ステップ３の関数の設定画面にて関数名（任意）、説明（任意）を入力し、ランタイムは「Python2.7」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/632daa1f-7bb8-499d-8291-a1d23c427140.png)
Lambda関数のコードでは、コードエントリタイプを「.ZIPファイルをアップロード」を選択して、「関数パッケージの作成」の手順で作成したZIPファイルをアップロードします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/7074a2d0-68a6-f772-105d-0ffd8bd87608.png)
Lambda関数ハンドラおよびロールでは、GitHubからダウンロードしたPythonコードの拡張子を除いたファイル名（サンプル：pycdatakintonedemo）とハンドラ名（サンプル：Main）をドットで繋いだ名前を設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/07b8fc8c-3ec7-6704-5e63-790be0d28699.png)
詳細設定にて、タイムアウトの時間を適切な値（例：1分）に設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/3d0b02ea-91d8-3513-f65c-74dc0b002300.png)

ここまで設定し終わったら最下部の「次へ」ボタンをクリックして、確認画面で問題がなければ「関数の作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/10d630d9-bc40-806f-2e02-915e5e0d9a17.png)
関数が作成されましたことを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/816bac67-13d2-a4ed-64fc-ef7b158d54bd.png)

#Lambda関数の実行
##テストイベントの作成
「アクション」から「テストイベントの設定」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/f2849ae6-72da-dbc4-30f6-4bc9a1c716c8.png)

サンプルイベントテンプレートはHelloWorldのままで、4つのパラメータの値を設定します。

```sample.json
{
  "conn_str": "Url=https://***.cybozu.com;User=***;Password=***;",
  "option": "1",
  "table": "table_name",
  "sql": "SELECT * FROM table_name"
}
```

「conn_str」はkintoneへの接続文字列です。最低限、Url、User、Passwordの３つが必要となります。
「option」は以下の4通りです。2,3の場合は「table」も指定してください。4の場合は「sql」も指定してください。

・ option:1,               - List all the tables in the database'
・ option:2, table:name    - List all the columns for a specific table'
・ option:3, table:name    - Select data from table'
・ option:4, sql:statement - Custom SQL Query'

![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/705efaf4-9157-108b-ee65-c2153fd36f6f.png)

##テスト実行
それでは「テスト」ボタンをクリックして実行してみます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/54283d80-1a53-af80-faad-6b8589c2fe04.png)

実行結果に「成功」と表示されればOKです。「詳細」をクリックしてログ出力内の「ここをクリック」してCloudWatch内のログをみてみましょう。エラーが発生した場合はログの内容を確認してください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/ca00a6e3-40b9-4aa7-e888-ac2bf1d550e7.png)

ログストリームを選択して、メッセージ内容をみてみるとkintone内のアプリの一覧が出力されていることを確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/71853800-4437-ff01-bb21-3774d01970c1.png)

続いて、テストイベントを開き、「option」に「3」（テーブルデータの表示）をセットして「table」にkintone内に存在するアプリ名（例：CDataJapanSKU）をセットして、「保存してテスト」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/35f40476-2417-8f60-3030-d9a114b43156.png)

CloudWatch内のログをもう一度開いてみると、kintoneアプリ内のデータ（例：CDataJapanSKU内の商品データ）が表示されていることを確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123181/d1e4645e-5539-79f8-f538-1899974b8041.png)

#まとめ

CData ODBC Driverを用いたAWS LambdaからのPythonによるkintone連携の手順でした。CData ODBC Driverはkintoneの他にも９０を超えるSaas、アプリケーション、データベースに接続可能です。各種ODBCドライバは[こちら](http://www.cdata.com/odbc/)のWebページからダウンロードしてご試用ください。