OpenStack + Common Lisp

5,650 views

Published on

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,650
On SlideShare
0
From Embeds
0
Number of Embeds
2,985
Actions
Shares
0
Downloads
27
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

OpenStack + Common Lisp

  1. 1. (Advent Calendar 2012 JP) (openstack) (Open source software to build public and private clouds.) ( イヴの夜は OpenStack を (Common Lisp) でキメる! ) ( まあ、 ( キメなくてもいいんだけどね・・・ ( いや、キメてください。 ( お願いします。 )))) (2012.12.24 @irix_jp) 1
  2. 2. m9(^Д^) プギャーwww● イヴの夜にこんな資料作ってるヤツwww orz 2
  3. 3. 気を取り直して・・・ ● OpenStack は全ての命令を API サーバが「 ReST 形式」で受付けます。 Client Command DBAdmin orUsers OpenStack Client Horizon Component Library (python) API Server AMQP OpenStack Component Other External Program Software 3
  4. 4. ということは・・・● HTTP のリクエストが投げられれば、何にからでも 操作可能! ● そう、 Common Lisp からでも! – おまけで Ruby とか Perl とか Curl とか Wget でも。 4
  5. 5. Common Lisp のはじめ方● まずは好みの処理系を選ぶ ● Common Lisp は厳密には「言語」では無く、「仕様」 ● この仕様に準拠して実装されたプログラムを 「 Common Lisp 処理系」と呼びます。 ● 代表的な処理系 – Clozure CL ・・・おすすめ!( Win/Linux/Mac で動く) – SBCL ・・・処理が早い。 Linux なら安心。 Win だと苦しい。 – Allegro CL ・・・商用。評価版あり。リッチな IDE 付き。 – Lisp Works ・・・商用。使った事無いです。 より詳しい情報はこちら http://ja.wikipedia.org/wiki/Common_Lisp 5
  6. 6. 早速動かす● Clozure CL を使います( Linux 想定) ● ここから DL – http://ccl.clozure.com/download.html ● 自分の環境に合わせたものを選択 ● 解凍して適当に配置。 – /opt/ccl とか ● とりあえず実行してみる – /opt/ccl/lx86cl   ・・・ 32bit 環境はこちら – /opt/ccl/lx86cl64  ・・・64 bit 環境はこちら 6
  7. 7. 早速動かす● これで OK $ /opt/ccl/lx86cl64 Welcome to Clozure Common Lisp Version 1.8-r15286M (LinuxX8664)! ? ← これがプロンプト● とりあえず Hello World ? "Hello World" "Hello World"● 超簡単!!! 7
  8. 8. もう少し Lisp っぽく● format 関数を使った例 ? (format t "~a" "Hello World") Hello World NIL● エラーしたら ? (format r "~a" "Hello World") > Error: Unbound variable: R > While executing: CCL::CHEAP-EVAL-IN-ENVIRONMENT, in process listener(1). > Type :GO to continue, :POP to abort, :R for a list of available restarts. > If continued: Retry getting the value of R. > Type :? for other options. 1 > q Debbuger が起動するので冷静に「 q 」● 終了 ? (quit) 8
  9. 9. プロンプト入力が低機能杉www● 補完も履歴参照もできねーよwww ● 案ずるな、バッチ実行できる。 – 適当なファイルを準備 ● test.lisp (format t "~a" "Hello World") – コマンドラインから $ /opt/ccl/lx86cl64 -Q -b < test.lisp Hello World NIL ● -Q だんまりモード。プロンプトや起動メッセージを表示しない。 ● -b バッチモード。標準入力から受け取った式を評価して終了。 9
  10. 10. エディタ● Emacs でも、 VIM でも xxxx.lisp とファイル名をつ ければ、 Lisp Mode になるので好きな方で。● Emacs だと Slime という Lisp 開発をインタラク ティブに行える便利ツールがあるので、慣れてきた らコレを使うのがおすすめ。 ● Superior Lisp Interaction Mode for Emacs – http://common-lisp.net/project/slime/ ● Emacs ユーザなら Lispbox を使うのもあり。いろいろ 揃った All in One 環境 – http://common-lisp.net/project/lispbox/ 10
  11. 11. パッケージ管理● Python に PIP 、 Ruby に GEM 、 Perl に CPAN が あるように・・・● Common Lisp にも Quiacklisp というパッケージ 管理の仕組みがあります。 ● サイトはこちら – http://www.quicklisp.org/ 11
  12. 12. パッケージ管理● 導入は簡単 $ wget http://beta.quicklisp.org/quicklisp.lisp $ /opt/ccl/lx86cl64 Welcome to Clozure Common Lisp Version 1.8-r15286M (LinuxX8664)! ? ? (load "quicklisp.lisp") . .. ... ? (quicklisp-quickstart:install) . プロキシを経由する場合 (quicklisp-quickstart:install :proxy "http://192.168.128.254:8080") .. ... ? (ql:add-to-init-file) . .. ... ? (quit) 12
  13. 13. パッケージの導入● HTTP リクエストを投げるパッケージを導入 ? (ql:quickload :drakma) ● 初回のみ、パッケージの DL が実行される。● ついでに json パッケージも ? (ql:quickload :cl-json)● パッケージの検索 ? (ql:system-apropos "json") 13
  14. 14. drakma がエラーするヤツ● OS の環境によってはエラーが起きる [package cl+ssl] > Error: Unable to load any of the alternatives: > ("libssl.so.1.0.0" "libssl.so.0.9.8" "libssl.so" "libssl.so.4") > While executing: CFFI::FL-ERROR, in process listener(1). > Type :POP to abort, :R for a list of available restarts. > Type :? for other options.● libssl のファイル名の問題だけなので、適当にリン ク貼ればおk $ find / |grep libssl.so $ sudo ln -s /usr/lib64/libssl.so.10 /usr/lib64/libssl.so● 再実行 ? (ql:quickload :drakma) 14
  15. 15. ここから本編● ようやくだよ! 15
  16. 16. keystone へリクエストを投げる● とりあえず何の工夫も無く openstack.lisp (ql:quickload :drakma) (drakma:http-request "http://cc:5000/v2.0/tokens")● 実行すると当然エラー( 404 Not Found ) ● 指定されたリクエスト方式で、ヘッダ・データをちゃんと 含める必要がある。 16
  17. 17. keystone へリクエストを投げる● ちゃんと投げてみる ● 最初の方はおまじない ; ライブラリのロード (ql:quickload :drakma) ; HTTP クライアント (ql:quickload :cl-json) ; JSON パーサ ; デフォルトエンコードを指定 (setf drakma:*drakma-default-external-format* :utf-8) ; application/json をテキストとして扱う (pushnew (cons "application" "json") drakma:*text-content-types* :test #equal) (drakma:http-request "http://192.168.100.52:5000/v2.0/tokens" :content-type "application/json" :method :post :content "{"auth": {"tenantName": "demo", "passwordCredentials": {"username": "demo", "password": "openstack"}}}") 17
  18. 18. keystone へリクエストを投げる● keystone に認証をかけるには ● Content-tyep: application/json で、 ● データ部に json 形式で以下の内容を含めて、 – テナント名 – ユーザ名 – パスワード ● http://keystone-address:5000/v2.0/tokens – へ POST する。 18
  19. 19. keystone へリクエストを投げる● もう少しスマートに、 ● まず json を生成する関数を作ってみる ; ライブラリのロード (ql:quickload :drakma) ; HTTP クライアント (ql:quickload :cl-json) ; JSON パーサ ; デフォルトエンコードを指定 (setf drakma:*drakma-default-external-format* :utf-8) ; application/json をテキストとして扱う (pushnew (cons "application" "json") drakma:*text-content-types* :test #equal) (defun auth-create-json (tenantName username password) "keystone へ送る JSON を生成する " (json:encode-json-to-string `(("auth" ("tenantName" . ,tenantName) ("passwordCredentials" ("username" . ,username) ("password" . ,password)))))) (auth-create-json "demo" "demo" "openstack") 19
  20. 20. keystone へリクエストを投げる● 前のページの続き ● リクエストを投げる関数も作る (defun auth-post-json (url tenantName username password) "JSON を keystone へ送付し、認証情報を得る " (drakma:http-request url :content-type "application/json" :method :post :content (auth-create-json tenantName username password))) (auth-post-json "http://192.168.100.52:5000/v2.0/tokens" "demo" "demo" "openstack") ● これを実行すると、それっぽい JSON が返ってくる(と思 う 20
  21. 21. ここまでのコード● これ以降のコードはこの下に追記していく ● openstack.lisp ; ライブラリのロード (ql:quickload :drakma) ; HTTP クライアント (ql:quickload :cl-json) ; JSON パーサ ; デフォルトエンコードを指定 (setf drakma:*drakma-default-external-format* :utf-8) ; application/json をテキストとして扱う (pushnew (cons "application" "json") drakma:*text-content-types* :test #equal) (defun auth-create-json (tenantName username password) "keystone へ送る JSON を生成する " (json:encode-json-to-string `(("auth" ("tenantName" . ,tenantName) ("passwordCredentials" ("username" . ,username) ("password" . ,password)))))) (defun auth-post-json (url tenantName username password) "JSON を keystone へ送付し、認証情報を得る " (drakma:http-request url :content-type "application/json" :method :post :content (auth-create-json tenantName username password))) 21
  22. 22. 認証結果 ● さっきの関数を実行してみると・・・(auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "password")"{"access": {"token": {"expires": "2012-12-21T15:24:01Z", "id": "27ae438762604ffe824fdc5a12b08b4e", "tenant":{"enabled":true, "description": "", "name": "demo", "id": "ad6bc57213b04c7f867aadbab97519e3"}}, "serviceCatalog":[{"endpoints": [{"adminURL": "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3", "region": "RegionOne","internalURL": "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3", "id": "aecda37bbc384097bb38e0ac2de8264a", "publicURL": "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"}], "endpoints_links":[], "type": "compute", "name": "nova"}, {"endpoints":[{"adminURL": "http://157.7.133.23:9696/", "region": "RegionOne", "internalURL": "http://157.7.133.23:9696/", "id": "1753832bfd754f7f8d61e13dda948be6", "publicURL": "http://157.7.133.23:9696/"}], "endpoints_links": [], "type":"network", "name": "quantum"}, {"endpoints": [{"adminURL": "http://157.7.133.23:9292", "region": "RegionOne","internalURL": "http://157.7.133.23:9292", "id": "940eef79e0ad4716b5035cfcf77a8916", "publicURL": "http://157.7.133.23:9292"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints":[{"adminURL": "http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3", "region": "RegionOne", "internalURL":"http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3", "id": "557a9527f0a94d908613260107738e3a", "publicURL": "http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3"}], "endpoints_links":[], "type": "volume", "name": "cinder"}, {"endpoints": [{"adminURL": "http://157.7.133.23:8773/services/Admin","region": "RegionOne", "internalURL": "http://157.7.133.23:8773/services/Cloud", "id": "dadd326ef5904cecaf4a058a734003f2", "publicURL": "http://157.7.133.23:8773/services/Cloud"}], "endpoints_links": [], "type": "ec2", "name":"ec2"}, {"endpoints":[{"adminURL": "http://157.7.133.23:35357/v2.0", "region": "RegionOne", "internalURL": "http://157.7.133.23:5000/v2.0", "id": "1d763970d0b24d49be57eb7368044fd6", "publicURL": "http://157.7.133.23:5000/v2.0"}], "endpoints_links":[], "type": "identity", "name": "keystone"}], "user": {"username": "demo", "roles_links":[], "id": "5f003e1815ea4f76991a8b824402a918", "roles": [{"name": "Member"}], "name": "demo"}, "metadata":{"is_admin": 0, "roles": ["f55588c94cba4cce9f1cf2e4a15f490b"]}}}" できてるっぽい 22
  23. 23. JSON をパースする ● cl-json のデコーダーを使って、リスト形式へ変換(json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$"))((:ACCESS (:TOKEN (:EXPIRES . "2012-12-21T15:45:52Z") (:ID . "729038529271455c849b8ce5249d1af3") (:TENANT (:ENABLED . T)(:DESCRIPTION . "") (:NAME . "demo") (:ID . "ad6bc57213b04c7f867aadbab97519e3"))) (:SERVICE-CATALOG ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "compute") (:NAME . "nova"))((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9696/") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:9696/") (:ID . "1753832bfd754f7f8d61e13dda948be6") (:PUBLIC-+URL+ . "http://157.7.133.23:9696/")))(:ENDPOINTS--LINKS) (:TYPE . "network") (:NAME . "quantum")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9292")(:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9292") (:ID . "940eef79e0ad4716b5035cfcf77a8916") (:PUBLIC-+URL+ . "http://157.7.133.23:9292"))) (:ENDPOINTS--LINKS) (:TYPE . "image") (:NAME . "glance")) ((:ENDPOINTS ((:ADMIN-+URL+ ."http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3") (:ID . "557a9527f0a94d908613260107738e3a") (:PUBLIC-+URL+ ."http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "volume") (:NAME . "cinder"))((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8773/services/Admin") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8773/services/Cloud") (:ID . "dadd326ef5904cecaf4a058a734003f2") (:PUBLIC-+URL+ ."http://157.7.133.23:8773/services/Cloud"))) (:ENDPOINTS--LINKS) (:TYPE . "ec2") (:NAME . "ec2")) ((:ENDPOINTS ((:ADMIN-+URL+. "http://157.7.133.23:35357/v2.0") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:5000/v2.0") (:ID ."1d763970d0b24d49be57eb7368044fd6") (:PUBLIC-+URL+ . "http://157.7.133.23:5000/v2.0"))) (:ENDPOINTS--LINKS) (:TYPE ."identity") (:NAME . "keystone"))) (:USER (:USERNAME . "demo") (:ROLES--LINKS) (:ID . "5f003e1815ea4f76991a8b824402a918")(:ROLES ((:NAME . "Member"))) (:NAME . "demo")) (:METADATA (:IS--ADMIN . 0) (:ROLES "f55588c94cba4cce9f1cf2e4a15f490b")))) このリストをよく見ると、以下のような構造になっている。 ((:ACCESS (:TOKEN ....) (:SERVICE-CATALOG...) (:USER ...) (:METADATA..)) 23
  24. 24. リスト操作 ● First 関数でリストから最初の要素を取り出す(first (json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$")))(:ACCESS (:TOKEN (:EXPIRES . "2012-12-21T16:05:39Z") (:ID . "e79e348b0115468891a8c7bf752f4d64") (:TENANT (:ENABLED . T) (:DESCRIPTION . "") (:NAME ."demo") (:ID . "ad6bc57213b04c7f867aadbab97519e3"))) (:SERVICE-CATALOG ((:ENDPOINTS ((:ADMIN-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "compute") (:NAME . "nova")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9696/") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9696/") (:ID ."1753832bfd754f7f8d61e13dda948be6") (:PUBLIC-+URL+ . "http://157.7.133.23:9696/"))) (:ENDPOINTS--LINKS) (:TYPE . "network") (:NAME . "quantum"))((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9292") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9292") (:ID ."940eef79e0ad4716b5035cfcf77a8916") (:PUBLIC-+URL+ . "http://157.7.133.23:9292"))) (:ENDPOINTS--LINKS) (:TYPE . "image") (:NAME . "glance"))((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3") (:ID . "557a9527f0a94d908613260107738e3a") (:PUBLIC-+URL+ ."http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "volume") (:NAME . "cinder")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8773/services/Admin") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:8773/services/Cloud") (:ID ."dadd326ef5904cecaf4a058a734003f2") (:PUBLIC-+URL+ . "http://157.7.133.23:8773/services/Cloud"))) (:ENDPOINTS--LINKS) (:TYPE . "ec2") (:NAME ."ec2")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:35357/v2.0") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:5000/v2.0")(:ID . "1d763970d0b24d49be57eb7368044fd6") (:PUBLIC-+URL+ . "http://157.7.133.23:5000/v2.0"))) (:ENDPOINTS--LINKS) (:TYPE . "identity") (:NAME ."keystone"))) (:USER (:USERNAME . "demo") (:ROLES--LINKS) (:ID . "5f003e1815ea4f76991a8b824402a918") (:ROLES ((:NAME . "Member"))) (:NAME . "demo"))(:METADATA (:IS--ADMIN . 0) (:ROLES "f55588c94cba4cce9f1cf2e4a15f490b"))) (:ACCESS (:TOKEN ....) (:SERVICE-CATALOG...) (:USER ...) (:METADATA..) 一つカッコが取れる。 24
  25. 25. リスト操作 ● さらに一つづつ取り出してみると(first (first (json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$"))))→ :ACCESS(second (first (json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$"))))→ (:TOKEN (:EXPIRES . "2012-12-21T16:08:44Z") (:ID . "e23f631ca4c74d64970f3bc21c21147c") (:TENANT (:ENABLED . T) (:DESCRIPTION . "") (:NAME ."demo") (:ID . "ad6bc57213b04c7f867aadbab97519e3")))(fourth (first (json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$"))))→ (:USER (:USERNAME . "demo") (:ROLES--LINKS) (:ID . "5f003e1815ea4f76991a8b824402a918") (:ROLES ((:NAME . "Member"))) (:NAME . "demo")) (:ACCESS (:TOKEN ....) (:SERVICE-CATALOG...) (:USER ...) (:METADATA..) と、上から一つずつ要素が取り出せる。 25
  26. 26. リスト操作 ● ここから TOKEN を取り出してみる。(cdr (third (second (first (json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$"))))))→ "ce26db6126e342a8b604019463f34bb7" ● 芸はないけど、こんな感じで TOKEN が取り出せ る。 ● しかしこの方法は場所決め打ちで取り出しているので、 汎用性がなさすぎる。 – TOKEN の場所が ● :TOKEN → :ID にあることを利用すると・・・ 26
  27. 27. ループと判定 ● ループで回して if で判定すると TOKEN が取得で きる。(block exit (dolist (x (first (json:decode-json-from-string (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$")))) (if (listp x) (if (eql :TOKEN (first x)) (dolist (y x) (if (listp y) (if (eql :ID (first y)) (return-from exit (cdr y)))))))))→ "e2f35b1dca014a38a4eb9afb55eb752d" 27
  28. 28. TOKEN を取得する関数 ● JSON を受け取り、 TOKEN を返す関数(defun auth-return-token-from-json (json) "keystone が返す JSON を受け取り、 TOKEN を抽出して返す関数 " (block exit (dolist (x (first (json:decode-json-from-string json))) (if (listp x) (if (eql :TOKEN (first x)) (dolist (y x) (if (listp y) (if (eql :ID (first y)) (return-from exit (cdr y)))))))))) ● 実行すると・・・(auth-return-token-from-json (auth-post-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$"))→ "c71bb1f6e88b4032a4413c94a7f2bd46" 28
  29. 29. もう少し汎用的に ● keystone から JSON を受け取って、リスト変換して 返す関数 ● リスト変換された JSON とキーを受け取ってキーが 存在するリストを返す関数(defun auth-return-list-from-json (url tenantName username password) "keystone から認証 JSON を受け取り、リストへ変換して返す " (first (json:decode-json-from-string (auth-post-json url tenantName username password))))(defun auth-return-list-from-json (json key) "json とキーを一つ受け取り、キーがマッチしたリストを返す " (block exit (dolist (x (first (json:decode-json-from-string json))) (if (listp x) (if (eql key (first x)) (return-from exit x))))))(auth-return-key-from-json (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :TOKEN)(:TOKEN (:EXPIRES . "2012-12-24T14:52:59Z") (:ID . "f116354e97494b52a1f06ad04e45580c") (:TENANT (:ENABLED . T) (:DESCRIPTION . "") (:NAME ."demo") (:ID . "ad6bc57213b04c7f867aadbab97519e3"))) 29
  30. 30. 任意のデータを取り出す(defun auth-return-specified-key-from-json-list (json-list &rest keys) " 指定されたキーを取り出す " (let ((x json-list)) (dolist (key keys) (setf x (auth-return-key-from-json x key))) x))(auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :TOKEN :ID)→ (:ID . "68013432d7274ad78b3630461cb385ef")(auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG)→ (:SERVICE-CATALOG ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne")(:INTERNAL-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "compute") (:NAME . "nova")) ((:ENDPOINTS((:ADMIN-+URL+ . "http://157.7.133.23:9696/") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9696/") (:ID ."1753832bfd754f7f8d61e13dda948be6") (:PUBLIC-+URL+ . "http://157.7.133.23:9696/"))) (:ENDPOINTS--LINKS) (:TYPE . "network") (:NAME ."quantum")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9292") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9292")(:ID . "940eef79e0ad4716b5035cfcf77a8916") (:PUBLIC-+URL+ . "http://157.7.133.23:9292"))) (:ENDPOINTS--LINKS) (:TYPE . "image") (:NAME ."glance")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3") (:ID . "557a9527f0a94d908613260107738e3a") (:PUBLIC-+URL+ ."http://157.7.133.23:8776/v1/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "volume") (:NAME . "cinder")) ((:ENDPOINTS((:ADMIN-+URL+ . "http://157.7.133.23:8773/services/Admin") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8773/services/Cloud") (:ID . "dadd326ef5904cecaf4a058a734003f2") (:PUBLIC-+URL+ ."http://157.7.133.23:8773/services/Cloud"))) (:ENDPOINTS--LINKS) (:TYPE . "ec2") (:NAME . "ec2")) ((:ENDPOINTS ((:ADMIN-+URL+ ."http://157.7.133.23:35357/v2.0") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:5000/v2.0") (:ID ."1d763970d0b24d49be57eb7368044fd6") (:PUBLIC-+URL+ . "http://157.7.133.23:5000/v2.0"))) (:ENDPOINTS--LINKS) (:TYPE . "identity") (:NAME ."keystone")))(auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :METADATA :ROLES)→ (:ROLES "f55588c94cba4cce9f1cf2e4a15f490b")(auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :USER :USERNAME)→ (:USERNAME . "demo") 30
  31. 31. エンドポイントの抽出 ● auth-return-specified-key-from-json-list 関数 で大体のデータは取り出せるが・・・ ● ENDPOINT はデータ形式がめんどくさい(:SERVICE-CATALOG ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS—LINKS) (:TYPE . "compute") (:NAME . "nova")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9696/") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9696/") (:ID . "1753832bfd754f7f8d61e13dda948be6") (:PUBLIC-+URL+ . "http://157.7.133.23:9696/"))) (:ENDPOINTS--LINKS) (:TYPE . "network") (:NAME . "quantum")) ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9292") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9292") (:ID . "940eef79e0ad4716b5035cfcf77a8916") (:PUBLIC-+URL+ . "http://157.7.133.23:9292"))) (:ENDPOINTS--LINKS) (:TYPE . "image") (:NAME . "glance")) ....... 31
  32. 32. エンドポイントの抽出 ● まずエンドポイントの要素1つを調べて、それが欲し いデータならば t(true) を返す関数を作る(defun auth-check-endpoint-name (endpoint-list servicename) "1 つのエンドポイントを受け取り、それが servicename にマッチするエンドポイントならば t" (if (consp endpoint-list) (dolist (x endpoint-list) (if (consp x) (if (eql (first x) :NAME) (progn (if (string-equal (cdr x) servicename) (return-from auth-check-endpoint-name t)))))) nil))(second (auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG))→ ((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "compute") (:NAME . "nova"))(auth-check-endpoint-name (second (auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG)) "nova")→ T(auth-check-endpoint-name (second (auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG)) "quantum")→ NIL 32
  33. 33. エンドポイントの抽出 ● マップ関数を使ったリスト操作 ● マップ関数を使うとリストの要素に対して、一括した操作 が行える。 ● 以下は指定した文字列にマッチするサービス名を含む 要素のみを残して、残りを破棄する例CL-USER> (remove-if-not #(lambda (x) (auth-check-endpoint-name x "nova")) (auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG))→ (((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "compute") (:NAME . "nova")))CL-USER> (remove-if-not #(lambda (x) (auth-check-endpoint-name x "quantum")) (auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG))→ (((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9696/") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9696/") (:ID ."1753832bfd754f7f8d61e13dda948be6") (:PUBLIC-+URL+ . "http://157.7.133.23:9696/"))) (:ENDPOINTS--LINKS) (:TYPE . "network") (:NAME ."quantum")))CL-USER> (remove-if-not #(lambda (x) (auth-check-endpoint-name x "notmatch")) (auth-return-specified-key-from-json-list (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") :SERVICE-CATALOG))→ NIL 33
  34. 34. エンドポイントの抽出 ● 関数化しておく(defun auth-return-specified-endpoint (json-list servicename) " サービスカタログから指定のエンドポイントのみを抽出する " (remove-if-not #(lambda (x) (auth-check-endpoint-name x servicename)) (auth-return-specified-key-from-json-list json-list :SERVICE-CATALOG)))CL-USER> (auth-return-specified-endpoint (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") "nova")→ (((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:REGION . "RegionOne") (:INTERNAL-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3") (:ID . "aecda37bbc384097bb38e0ac2de8264a") (:PUBLIC-+URL+ ."http://157.7.133.23:8774/v2/ad6bc57213b04c7f867aadbab97519e3"))) (:ENDPOINTS--LINKS) (:TYPE . "compute") (:NAME . "nova")))CL-USER> (auth-return-specified-endpoint (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") "quantum")→ (((:ENDPOINTS ((:ADMIN-+URL+ . "http://157.7.133.23:9696/") (:REGION . "RegionOne") (:INTERNAL-+URL+ . "http://157.7.133.23:9696/") (:ID ."1753832bfd754f7f8d61e13dda948be6") (:PUBLIC-+URL+ . "http://157.7.133.23:9696/"))) (:ENDPOINTS--LINKS) (:TYPE . "network") (:NAME ."quantum")))CL-USER> (auth-return-specified-endpoint (auth-return-list-from-json "http://cc-myvps:5000/v2.0/tokens" "demo" "demo" "^openstack2012$") "nomatch")→ NIL 34
  35. 35. コラム1:マップ関数● 「マップ」というのは、 Hadoop の MapReduce の Map と似た処理。 ● (1 2 3 4 5) みたいなリストの要素全てに計算を適用さ せる関数の事をマップ関数と読んでいる。 ● 全ての要素に x 2を適用。 – (mapcar #(lambda (x) (* x 2)) (1 2 3 4 5)) → (1 4 6 8 10) 35
  36. 36. コラム2:ベクター● 同じ型の要素を持ったリスト(配列)をベクターと呼 ぶ。 ● (1 2 3 4 5) や ("a" "b" "c" "d" "e")● このベクターに対して map 関数のような処理を実 行する時に、 CPU が各要素に対する演算を並列に 行えるように作られた CPU をベクタープロセッサと いう ● ベクターデータに対する並列計算が得意な CPU ● 対して、スカラープロセッサは for で要素を一つずつ取 り出して計算されるイメージ。 10 年以上前に聞いた話で、記憶も曖昧なので間違ってるかも 36
  37. 37. 後少し ● ここまでくると、コレまでの関数の組合せで、ほぼ任 意のデータが取得できるようになる。 ● いったんコレまでのコードを整理; ライブラリのロード(ql:quickload :drakma) ; HTTP クライアント(ql:quickload :cl-json) ; JSON パーサ; デフォルトエンコードを指定(setf drakma:*drakma-default-external-format* :utf-8); application/json をテキストとして扱う(pushnew (cons "application" "json") drakma:*text-content-types* :test #equal)(defun auth-create-json (tenantName username password) "keystone へ送る JSON を生成する " (json:encode-json-to-string `(("auth" ("tenantName" . ,tenantName) ("passwordCredentials" ("username" . ,username) ("password" . ,password))))))(defun auth-post-json (url tenantName username password) "JSON を keystone へ送付し、認証情報を得る " (drakma:http-request url :content-type "application/json" :method :post :content (auth-create-json tenantName username password))) 37
  38. 38. 後少し ● 続き(defun auth-return-list-from-json (url tenantName username password) "keystone から認証 JSON を受け取り、リストへ変換して返す " (first (json:decode-json-from-string (auth-post-json url tenantName username password))))(defun auth-return-key-from-json (json-list key) " リスト変換された json とキーを一つ受け取り、キーがマッチしたリストを返す " (block exit (dolist (x json-list) (if (listp x) (if (eql key (first x)) (return-from exit x))))))(defun auth-return-specified-key-from-json-list (json-list &rest keys) " 指定されたキーを取り出す " (let ((x json-list)) (dolist (key keys) (setf x (auth-return-key-from-json x key))) x)) 38
  39. 39. 後少し ● 続き(defun auth-check-endpoint-name (endpoint-list servicename) "1 つのエンドポイントを受け取り、それが servicename にマッチするエンドポイントならば t" (if (consp endpoint-list) (dolist (x endpoint-list) (if (consp x) (if (eql (first x) :NAME) (progn (if (string-equal (cdr x) servicename) (return-from auth-check-endpoint-name t)))))) nil))(defun auth-return-specified-endpoint (json-list servicename) " サービスカタログから指定のエンドポイントのみを抽出する " (remove-if-not #(lambda (x) (auth-check-endpoint-name x servicename)) (auth-return-specified-key-from-json-list json-list :SERVICE-CATALOG))) 39
  40. 40. これまでに出てきたこと● 以下の基礎 ● リスト操作 ● 関数 ● マップ ● 条件分岐 ● ループ 40
  41. 41. さらに学習するには● マクロ● コンディション(例外処理)● CLOS (オブジェクト)● パッケージ などなど・・・ 41
  42. 42. とりあえずまとめ● 今回は JSON を一旦リストへ変換して、様々なリス ト操作を試してみました。● マクロやオブジェクトを使うともっと効率的にデータ 抽出が可能です。● もしこれで Common Lisp に興味を持った方がい れば、後述の参考書籍を見て、本格的に学習してみ てください。 42
  43. 43. おすすめ書籍● 実践 Common Lisp ● Practical Common Lisp● 実用 Common Lisp ● Paradigms of Artificial Intelligence Programming● 計算機プログラムの構造と解釈 ● Structure and Interpretation of Computer Programs – 英語版がオンラインで読めます。 – http://mitpress.mit.edu/sicp/● On Lisp● LET OVER LAMBD 43
  44. 44. おすすめサイト● xyzzy Lisp Programming ● http://www.geocities.jp/m_hiroi/xyzzy_lisp.html● 逆引き CommonLisp ● http://tips.lisp-users.org/common-lisp/● モダン Common Lisp ● http://dev.ariel-networks.com/wp/archives/tag/common-lisp 44

×