2013年12月24日火曜日

続:イブの夜はIaaSをCommon Lispでキめる!

2つのアドベントカレンダーの合同ネタです。

OpenStack Advent Calendar 2013 JP
Wakame Users Group Advent Calendar 2013

Common Lispの基礎に関しては2012年の OpenStack Advent Calendar 2012 JP 12/24 イヴの夜はOpenStackをCommon Lispでキメる!をご覧ください。

Common Lispを使って、OpenStackとWakame-VDCの操作を行いながら、Lispの世界を見ていきましょう。


操作対象クラウド
・OpenStack Havana
・Wakame-VDC (12.03?) ここから取得したイメージです。

■やること
・クラウド上での仮想マシンの作成(起動)

環境は、

・clozure CL
・drakma
・cl-json
・yason

となります。ライブラリの導入は quicklisp を使うのがお手軽です。

早速ですがコーディングしていきます(各種認証情報等は時間がないわかりやすくするために決め打ちしています


まず、Wakameで仮想マシンを起動する方法はこんな感じでAPIを叩きます。
(drakma:http-request "http://10.0.2.15:9001/api/12.03/instances"
                     :method :post
                     :parameters '(("hypervisor" . "openvz")
                                   ("image_id" . "wmi-centos1d64")
                                   ("cpu_cores" . "1")
                                   ("memory_size" . "1024"))
                     :additional-headers '(("X_VDC_ACCOUNT_UUID" . "a-shpoolxx")))
@hansode さん、@sparklegate さん、情報ありがとうございます。

*Wakame-VDCのAPIマニュアルは Core API reference に記載があります。ただし使用例が無いので、Wakame-VDC に含まれる mussel というシェルラッパーに -x オプションでデバッグ表示をさせてやるとわかりやすいかと思います。mussel を使うには namespace というもの指定する必要がありますが、これは mussel 配下のバージョンディレクトリに配置されているファイルの名前(.shを外したもの)になります。


対してOpenStackは、こんな感じです。
(drakma:http-request (concatenate 'string (get-nova-endpoint) "/servers")
                     :method :post
                     :content-type "application/json"
                     :additional-headers `(("X-Auth-Token" . ,(get-token))
                                           ("X-Auth-Project-Id" . "admin"))
                     :content (json:encode-json-to-string 
                               '(("server" 
                                  ("flavorRef" . "1")
                                  ("name" . "test")
                                  ("imageRef" . "4e4e224e-8cdb-4b69-ab24-2627bb1aa846")))))

使っている関数の定義はこちら
;; UTF関連の設定
(setf drakma:*drakma-default-external-format* :utf-8)
(pushnew (cons "application" "json") drakma:*text-content-types* :test #'equal)

;; 認証オブジェクトを取得
(defun get-auth-obj ()
  (yason:parse
   (multiple-value-bind (body-or-stream
                         status-code
                         headers
                         uri
                         stream
                         must-close
                         reason-phrase)
       (drakma:http-request "http://192.168.128.100:5000/v2.0/tokens"
                            :content-type "application/json"
                            :method       :post
                            :content      (json:encode-json-to-string 
                                           '(("auth" ("tenantName" . "admin")
                                              ("passwordCredentials" ("username" . "admin")
                                               ("password" . "openstack"))))))
     body-or-stream)))

(defun get-property-from-hash (hash &rest values)
  "get specified properties from hash"
  (let ((x hash))
    (dolist (y values)
      (setf x (gethash y x)))
    x))

(defun get-token ()
  (get-property-from-hash (get-auth-obj) "access" "token" "id"))

(defun get-nova-endpoint ()
  (get-property-from-hash
   (first (get-property-from-hash
           (loop for i in (get-property-from-hash (get-auth-obj) "access" "serviceCatalog")
              if (string= (get-property-from-hash i "type") "compute") return i)
           "endpoints"))
   "publicURL"))

このWakame、OpenStackに対して同じ操作でインスタンスが作成できる関数 boot-instance を作ってみます。
(defgeneric boot-instance (cloud-obj)
  (:documentation "boot a instance on OpenStack or Wakame"))

次にクラスを定義します。ここで定義の中身は重要でないので適当です。
(defclass wakame ()
  ((api-version  :initform nil :initarg :api-version  :accessor get-api-version)
   (auth-url     :initform nil :initarg :auth-url     :accessor get-auth-url)))

(defclass openstack ()
  ((api-version  :initform nil :initarg :api-version  :accessor get-api-version)
   (auth-url     :initform nil :initarg :auth-url     :accessor get-auth-url)))


最後にメソッドを定義します。
(defmethod boot-instance ((w wakame))
    (drakma:http-request "http://10.0.2.15:9001/api/12.03/instances"
                         :method :post
                         :parameters '(("hypervisor" . "openvz")
                                       ("image_id" . "wmi-centos1d64")
                                       ("cpu_cores" . "1")
                                       ("memory_size" . "1024"))
                         :additional-headers '(("X_VDC_ACCOUNT_UUID" . "a-shpoolxx"))))

(defmethod boot-instance ((o openstack))
    (drakma:http-request (concatenate 'string (get-nova-endpoint) "/servers")
                         :method :post
                         :content-type "application/json"
                         :additional-headers `(("X-Auth-Token" . ,(get-token))
                                               ("X-Auth-Project-Id" . "admin"))
                         :content (json:encode-json-to-string 
                                   '(("server" 
                                      ("flavorRef" . "1")
                                      ("name" . "test")
                                      ("imageRef" . "4e4e224e-8cdb-4b69-ab24-2627bb1aa846"))))))

もうお気づきかと思いますが、この同一名のメソッドは与えられた引数によって挙動が変わります。
(boot-instance (make-instance 'openstack))
(boot-instance (make-instance 'wakame))

なんとかィズムってやつです。

Common Lispは古い言語ですが、こういったオブジェクト指向でのプログラミングも行うことが可能です。やはりLispは神の言語でしたね(嘘

0 件のコメント:

コメントを投稿