クリスマスはOpenStackをPythonワンライナーでキメる!
みなさんメリークリスマス。
おぷ☆すた関係者のみなさんの中にはクリスマス中止のお知らせが届いている方が多いかとは思いますが、2012年のJOSUG AdventCalendarネタの最後を飾るべくPythonのワンライナーでOpenStack-APIを叩く方法を書いてみます。
■keystone認証とOpenStack APIについて
REST-APIを利用してOpenStackを操作するためにはkeystoneからトークンをもらわなくてはなりません。
その仕組みについて、以下のスライドにまとめてみました。
☆OpenStack Study#9 JOSUG
python-keystoneclientが提供するモジュールを利用せずにPythonなどから利用したいと考えている奇特なひとがいたら参考までにどうぞ:)
■とりあえずワンライナーでふつうに書いてみる
必要となる情報を環境変数から取得し、keystoneで認証を受けて許可されたendpointへの接続情報を得るプログラムをワンライナーで書いてみます。
export OS_USERNAME=foo export OS_PASSWORD=bar export OS_TENANT_NAME=baz export OS_AUTH_URL="http://172.16.100.16:5000/v2.0"
- サービスのendpointとトークンIDを得るためのワンライナー(499文字)
ふつうに書いて、文を";"で繋くことによって1行にしてみた。
まぁふつうなので、あまり勝った気はしないし、おもしろくない。
python -c 'import re,os;from sys import stdout as s;from httplib import HTTPConnection as c;import json as j;e=os.environ;u=re.match(r"^(http|https)://(.*)(/.*)$",os.environ["OS_AUTH_URL"]).groups();x=c(u[1]);x.request("POST",u[2]+"/tokens","{\"auth\":{\"tenantName\":\"%s\",\"passwordCredentials\":{\"username\":\"%s\",\"password\":\"%s\"}}}"%(e["OS_TENANT_NAME"],e["OS_USERNAME"],e["OS_PASSWORD"]),{"Content-Type":"application/json"});s.write(j.dumps(j.load(x.getresponse()),indent=2));x.close()'
- とりあえずの結果
{ "access": { "token": { "expires": "2012-12-24T08:30:33Z", "id": "37b71bf29a9244d8a4214776ec5d467f", "tenant": { "id": "30ecf00d44f743499397b0b6ee3d7a19", "enabled": true, "description": "Default Tenant", "name": "baz" } }, "serviceCatalog": [ { "endpoints_links": [], "endpoints": [ { "adminURL": "http://172.16.100.16:8774/v2/30ecf00d44f743499397b0b6ee3d7a19", "region": "RegionOne", "publicURL": "http://172.16.100.16:8774/v2/30ecf00d44f743499397b0b6ee3d7a19", "internalURL": "http://172.16.100.16:8774/v2/30ecf00d44f743499397b0b6ee3d7a19", "id": "56c68763275145fe9403b70851973df9" } ], "type": "compute", "name": "nova" }, { "endpoints_links": [], "endpoints": [ { "adminURL": "http://172.16.100.16:9292/v1", "region": "RegionOne", "publicURL": "http://172.16.100.16:9292/v1", "internalURL": "http://172.16.100.16:9292/v1", "id": "0db60374f7d44b168c5c7169f3fa8c86" } ], "type": "image", "name": "glance" }, { "endpoints_links": [], "endpoints": [ { "adminURL": "http://172.16.100.16:8776/v1/30ecf00d44f743499397b0b6ee3d7a19", "region": "RegionOne", "publicURL": "http://172.16.100.16:8776/v1/30ecf00d44f743499397b0b6ee3d7a19", "internalURL": "http://172.16.100.16:8776/v1/30ecf00d44f743499397b0b6ee3d7a19", "id": "e4a933ca12d344e3bd762b8a2a8ca7d7" } ], "type": "volume", "name": "volume" }, { "endpoints_links": [], "endpoints": [ { "adminURL": "http://172.16.100.16:8773/services/Admin", "region": "RegionOne", "publicURL": "http://172.16.100.16:8773/services/Cloud", "internalURL": "http://172.16.100.16:8773/services/Cloud", "id": "e66ed5a7256145f191e66c4dfdb77626" } ], "type": "ec2", "name": "ec2" }, { "endpoints_links": [], "endpoints": [ { "adminURL": "http://172.16.100.16:8888/v1", "region": "RegionOne", "publicURL": "http://172.16.100.16:8888/v1/AUTH_30ecf00d44f743499397b0b6ee3d7a19", "internalURL": "http://172.16.100.16:8888/v1/AUTH_30ecf00d44f743499397b0b6ee3d7a19", "id": "7ef0dbf8462a4c0dbb25920cf6282f72" } ], "type": "object-store", "name": "swift" }, { "endpoints_links": [], "endpoints": [ { "adminURL": "http://172.16.100.16:35357/v2.0", "region": "RegionOne", "publicURL": "http://172.16.100.16:5000/v2.0", "internalURL": "http://172.16.100.16:5000/v2.0", "id": "38c31701d9bb4a6aa5d021424e2c90b8" } ], "type": "identity", "name": "keystone" } ], "user": { "username": "foo", "roles_links": [], "id": "cb8ad9145ff44b858ec4cf9c8848b305", "roles": [ { "name": "memberRole" } ], "name": "foo" }, "metadata": { "is_admin": 0, "roles": [ "8fb8ac9f2f2849588e02c4d1bef99a07" ] } } }
■もう少しワンライナーらしく
";"で繋いじゃワンライナーらしくないな...これじゃ元木さんにダメ出しされる。
そこで、
リスト内包表記、lambda、globals.__setitem__()やら"and"や"or"の評価を使って";"を可能な限り使わないように書き換えると、こんな感じ。
- importのところは、まぁとりあえず";"をつかってもいいかな。
- 代入文(=)を使用すると次の文との間を";"で繋ぐことになってワンライナーっぽくならないのでglobals().__setitem__()を使う。
- globals().__setitem__()をタプル()にするとFalseを返すので、これを利用してorで次のタプルと繋ぐ。
- 繰り替えされる処理は lambda で関数化
というあたりがポイントです。
■endpointリストを取得する
かろうじて700文字を切った。
- コード(699文字)
python -c 'import re,os;from sys import stdout as s;from httplib import HTTPConnection as c;import json as j;(globals().__setitem__("e",os.environ))or(globals().__setitem__("r",r"^(http|https)://(.*)(/.*)$"))or(globals().__setitem__("f0",lambda a,b:re.match(a,b).groups()))or(globals().__setitem__("f1",lambda b:c(b)))or(globals().__setitem__("p",f1(f0(r,e["OS_AUTH_URL"])[1])))or(p.request("POST",f0(r,e["OS_AUTH_URL"])[2]+"/tokens","{\"auth\":{\"tenantName\":\"%s\",\"passwordCredentials\":{\"username\":\"%s\",\"password\":\"%s\"}}}"%(e["OS_TENANT_NAME"],e["OS_USERNAME"],e["OS_PASSWORD"]),{"Content-Type":"application/json"}))or(s.write(j.dumps(j.load(p.getresponse()),indent=2)))or(p.close())'
- 結果はふつうのワンライナーと一緒なので割愛
■仮想マシンのリストを取得する
なんかもう1000文字越えちゃった。3日もしたら忘れちゃうのでデバッグ不可能。
- コード(1158文字)
python -c 'import re,os;from sys import stdout as s;from httplib import HTTPConnection as c;import json as j;(globals().__setitem__("e",os.environ))or(globals().__setitem__("r0",r"^(http|https)://(.*)(/.*)$"))or(globals().__setitem__("r1",r"^(http|https)://(.*:\d+)(/.*)$"))or(globals().__setitem__("f0",lambda a,b:re.match(a,b).groups()))or(globals().__setitem__("f1",lambda b:c(b)))or(globals().__setitem__("p",f1(f0(r0,e["OS_AUTH_URL"])[1])))or(p.request("POST",f0(r0,e["OS_AUTH_URL"])[2]+"/tokens","{\"auth\":{\"tenantName\":\"%s\",\"passwordCredentials\":{\"username\":\"%s\",\"password\":\"%s\"}}}"%(e["OS_TENANT_NAME"],e["OS_USERNAME"],e["OS_PASSWORD"]),{"Content-Type":"application/json"}))or(globals().__setitem__("z",j.load(p.getresponse())))or(globals().__setitem__("t",z["access"]["token"]["id"]))or(globals().__setitem__("u",f0(r1,[z["endpoints"][0]["publicURL"] for z in z["access"]["serviceCatalog"] if z["type"]=="compute"][0])))or(p.close())or(globals().__setitem__("p",f1(u[1])))or(p.request("GET",u[2]+"/servers","",{"Content-Type":"application/json","X-Auth-Token":t}))or(s.write(j.dumps(j.load(p.getresponse()),indent=2)))or(p.close())'
- 結果
なんか仮想マシンのリストが取得できてるっぽい。
{ "servers": [ { "id": "e852c933-9dcd-470f-a74c-712fc5a81976", "links": [ { "href": "http://172.16.100.16:8774/v2/30ecf00d44f743499397b0b6ee3d7a19/servers/e852c933-9dcd-470f-a74c-712fc5a81976", "rel": "self" }, { "href": "http://172.16.100.16:8774/30ecf00d44f743499397b0b6ee3d7a19/servers/e852c933-9dcd-470f-a74c-712fc5a81976", "rel": "bookmark" } ], "name": "vm02" }, { "id": "92d8c492-84f4-41b8-9e4a-87612a439c67", "links": [ { "href": "http://172.16.100.16:8774/v2/30ecf00d44f743499397b0b6ee3d7a19/servers/92d8c492-84f4-41b8-9e4a-87612a439c67", "rel": "self" }, { "href": "http://172.16.100.16:8774/30ecf00d44f743499397b0b6ee3d7a19/servers/92d8c492-84f4-41b8-9e4a-87612a439c67", "rel": "bookmark" } ], "name": "vm01" }, { "id": "98c7449c-acf2-4cfc-bdc1-55ee1e153c74", "links": [ { "href": "http://172.16.100.16:8774/v2/30ecf00d44f743499397b0b6ee3d7a19/servers/98c7449c-acf2-4cfc-bdc1-55ee1e153c74", "rel": "self" }, { "href": "http://172.16.100.16:8774/30ecf00d44f743499397b0b6ee3d7a19/servers/98c7449c-acf2-4cfc-bdc1-55ee1e153c74", "rel": "bookmark" } ], "name": "vm00" } ] }
という感じで、AdventCalendarの最終日を、こんな誰の役にも立たないワンライナーで締めくくるのもおぷ☆すたらしくていいかなぁ。
あ、lambda やリスト内包表記を使いこなす必要があるので、pythonの勉強をするにはちょっとは役に立つかも。