@blog.justoneplanet.info

日々勉強

AWS SDK for PythonでRoute53を操作する

以下のスクリプトをサーバー起動時に実行する事でホスト名を固定する事ができる。

#!/usr/bin/python
import sys
import boto
from boto.route53.record import ResourceRecordSets
import boto.ec2
import json
import pprint

key = 'your key'
secret = 'your secret'
domain_name = 'zone name'

document = boto.utils.get_instance_identity()["document"]
region = document["region"]
instance_id = document["instanceId"]

ec2_conn = boto.ec2.connect_to_region(
  region,
  aws_access_key_id=key,
  aws_secret_access_key=secret
)
reservations = ec2_conn.get_all_instances(instance_ids=[instance_id])
instance = reservations[0].instances[0]


route53_conn = boto.connect_route53(key, secret)
zone = route53_conn.get_zone(domain_name)
record = zone.get_cname(instance_id + "." + domain_name)
if record is not None:
  zone.delete_cname(instance_id + "." + domain_name)
zone.add_cname(instance_id + "." + domain_name, instance.public_dns_name)

以下の処理で自身のinstance IDを取得できる。

document = boto.utils.get_instance_identity()["document"]
region = document["region"]
instance_id = document["instanceId"]

以下のようにして削除する前に存在するか確認する必要がある。

record = zone.get_cname(instance_id + "." + domain_name)
if record is not None:
  zone.delete_cname(instance_id + "." + domain_name)

以下のようにしてCNAMEを追加する。

zone.add_cname(instance_id + "." + domain_name, instance.public_dns_name)

参考

AWS SDK for Pythonでインスタンスを起動してELBに追加する

pip

いれてなかったので。

wget http://peak.telecommunity.com/dist/ez_setup.py
sudo python ez_setup.py
sudo easy_install pip

何となくPythonのSDKを使うことにした。

botoのインストール

sudo pip install -U boto

code

import boto.ec2
import boto.ec2.elb

key = 'your key'
secret = 'your secret'
instance = 'instance'
balancer = 'balancer name'
region = 'region'
zone = 'zone'

connEc2 = boto.ec2.connect_to_region(
  region,
  aws_access_key_id=key,
  aws_secret_access_key=secret
)
connElb = boto.ec2.elb.connect_to_region(
  region,
  aws_access_key_id=key,
  aws_secret_access_key=secret
)

connEc2.start_instances(instance)
balancers = connElb.get_all_load_balancers(load_balancer_names=[balancer])
balancers[0].register_instances(instance)
balancers[0].enable_zones([zone])

反対の動作は以下のようになる。

balancers = connElb.get_all_load_balancers(load_balancer_names=[balancer])
balancers[0].deregister_instances(instance)
balancers[0].disable_zones([zone])
connEc2.stop_instances(instance)

MeCabをインストールする

■インストール

以下のコマンドでインストールできる。

wget https://mecab.googlecode.com/files/mecab-0.994.tar.gz
tar xvzf mecab-0.994.tar.gz
cd mecab-0.994
./configure
make
make install

辞書

utf-8で使用したいので以下のconfigureオプションを使用する。

wget https://mecab.googlecode.com/files/mecab-ipadic-2.7.0-20070801.tar.gz
tar xvzf mecab-ipadic-2.7.0-20070801.tar.gz 
cd mecab-ipadic-2.7.0-20070801
./configure --with-charset=utf8
make
make install

libiconvが必要になる。

macにインストールする

以下のコマンドでmacにインストールできる。

brew install mecab
brew install mecab-ipadic

■実行

以下のようにして使用する。

mecab
にわにはにわにわとりがいる
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
わに	名詞,一般,*,*,*,*,わに,ワニ,ワニ
はにわ	名詞,一般,*,*,*,*,はにわ,ハニワ,ハニワ
にわとり	名詞,一般,*,*,*,*,にわとり,ニワトリ,ニワトリ
が	助詞,格助詞,一般,*,*,*,が,ガ,ガ
いる	動詞,自立,*,*,一段,基本形,いる,イル,イル
EOS

ちょっと意地悪すぎたので入力を漢字にする。

庭には二羽鶏がいる
庭	名詞,一般,*,*,*,*,庭,ニワ,ニワ
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
二	名詞,数,*,*,*,*,二,ニ,ニ
羽	名詞,接尾,助数詞,*,*,*,羽,ワ,ワ
鶏	名詞,一般,*,*,*,*,鶏,ニワトリ,ニワトリ
が	助詞,格助詞,一般,*,*,*,が,ガ,ガ
いる	動詞,自立,*,*,一段,基本形,いる,イル,イル
EOS

正しく分類できた。

■Pythonで実行

久しぶりのPythonで遊ぶ。

python-devel

セットアップスクリプトを実行するのに必要になる。

yum insatll python-devel

mecab-python

以下のコマンドでインストールできる。

wget https://mecab.googlecode.com/files/mecab-python-0.996.tar.gz
tar xvzf mecab-python-0.98.tar.gz
cd mecab-python-0.98
python setup.py build
python setup.py install

テストスクリプトの文字コードを指定する。

vi test.py

2行目に以下のコードを加える。

# -*- coding: utf-8 -*-

以下のコマンドで実行する。

python test.py

■PHPで実行

php_mecabのインストール

su
pear channel-discover pecl.opendogs.org
pear install opendogs/mecab-beta
vim /private/etc/php.ini

Mountain LionにしたらPEARごと消滅してた

brew install autoconf

mecab.soが無いと言われて何もできなくなるので、php.iniを開いて一旦コメントアウトする。

su
vim /private/etc/php.ini

再度インストールする。

php /usr/lib/php/install-pear-nozlib.phar #PEARのインストール
pear channel-discover pecl.opendogs.org
pear install opendogs/mecab-beta #php-mecabのインストール
vim /private/etc/php.ini

ちなみに以下のようにパスを指定しないと

specify pathname to mecab-config [no] : /usr/local/bin

以下のようなエラーが出る。

checking for mecab-config... configure: error: not found
ERROR: `/var/tmp/mecab/configure --with-mecab=/usr' failed

以下の一行をextensionに付加する。

extension=mecab.so

実行

$mecab = new MeCab_Tagger();
for($node = $mecab->parseToNode($tag); $node; $node = $node->getNext()){
    var_dump($node->getSurface());
    var_dump($node->getFeature());
}

参考

ディフィー・ヘルマン鍵共有

盗聴の可能性のある通信路を使って、暗号鍵の共有を可能にする暗号プロトコルである。

#python
g = 3;
p = 2147483647;

#A
a = 11;
A = g**a % p;

#B
b = 10;
B = g**b % p;

#A
B**a % p;
#B
A**b % p;

pywebsocketをインストールする

■pywebsocket単体で動作させる

draft75

vi /home/pywebsocket-0.5.2/src/mod_pywebsocket/standalone.py

以下の部分を

parser.add_option('--allow-draft75', dest='allow_draft75',
                      action='store_true', default=False,
                      help='Allow draft 75 handshake')

以下のように変更する。

parser.add_option('--allow-draft75', dest='allow_draft75',
                      action='store_true', default=True,
                      help='Allow draft 75 handshake')

起動

python /home/pywebsocket-0.5.2/src/mod_pywebsocket/standalone.py -p 8800 -d /home/pywebsocket-0.5.2/src/example

WebSocketで8800ポートにアクセスする。(●´ω`●)

■apacheモジュールとして動作させる

http-devel

yum install http-devel

mod_python

wget http://archive.apache.org/dist/httpd/modpython/mod_python-3.3.1.tgz
tar xvzf mod_python-3.3.1.tgz
cd mod_python-3.3.1
./configure –with-apxs=/usr/sbin/apxs –with-python=/usr/bin/python
make
make install
設定
vi /etc/httpd/conf.d/python.conf

以下の記述の下に

LoadModule python_module modules/mod_python.so

以下の記述を付加する。

AddHandler mod_python .py

mod_pywebsocket

wget http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.5.2.tar.gz
tar xvzf mod_pywebsocket-0.5.2
cd pywebsocket-0.5.2/src
python setup.py build
python setup.py install
設定
vi /etc/httpd/conf.d/python_mod_pywebsocket.conf

以下を記述する。

<IfModule python_module>
    PythonPath "sys.path+['/usr/lib/python2.4/site-packages/']"
    PythonOption mod_pywebsocket.handler_root /var/www/html/wsh
    PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
    PythonOption mod_pywebsocket.allow_draft75 On
</IfModule>
/etc/init.d/httpd restart

起動

cp /home/pywebsocket-0.5.2/src/example/echo_wsh.py /var/www/html/wsh/

WebSocketクライアントで/echoにアクセスする。(●´ω`●)

■ベンチマーク

正確性は微妙だが100クライアントからの接続でのロードアベレージは、「node : pywebsoket(standalone) : pywebsoket(apache) = 0.15 : 0.25 : 0.8」となった。最初の2つの差は微妙だったが、Apacheモジュールとして動作させた時のリソースの消費量は明らかに大きいようだ。

DjangoでFormを作る

前回の記事のコードをベースに解説する。

■Formクラス

以下のようにFormクラスを継承して、ArticleSearchFormクラスを生成する。

# -*- coding: utf-8 -*-
from django import forms

class ArticleSearchForm(forms.Form):
    word = forms.CharField()

■ビュー関数

以下のようにsearch関数を定義する。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            articles = Article.objects.filter(title = form.cleaned_data['word'])
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

この場合、タイトルが完全一致しないと検索にヒットしないので以下のように__containsを付加する。これはSQLでいうLIKEのような役割だ。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            articles = Article.objects.filter(title__contains = form.cleaned_data['word'])
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

更に、条件をORで結合させたい場合は以下のようにする。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm
from django.db.models import Q

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            word     = form.cleaned_data['word']
            articles = Article.objects.filter(Q(title__contains = word) | Q(content__contains = word))
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

この辺はZendの方がSQLチックで直感的な気がする。

■テンプレート

以下のようにテンプレートを記述する。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>search</title>
<head>

<body>
<p>{{error_msg}}</p>
<form action="/search/" method="POST">
{% csrf_token %}
{{form.word}}
<input type="submit" value="search">
</form>
<br>
<table>
<tr>
<th>title</th>
<th>date</th>
</tr>
{% for article in articles %}
<tr>
<td>{{article.title}}</td>
<td>{{article.date}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

「setting.pyのMIDDLEWARE_CLASSESにdjango.middleware.csrf.CsrfViewMiddlewareが含まれている」かつ「ビュー関数でRequestContextを使用している」場合、{% csrf_token %}にCSRF対策用のトークンが埋め込まれる。

■URL設定

以下のように記述してURLを設定する。

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^article/(?P<id>[0-9]+)/$', 'dj.article.views.article'),
    (r'^search/$', 'dj.article.views.search'),
)

DjangoでHTTPレスポンスコードを変更する

前回のままだとURLのidが存在しなかった場合、500番を返すが本来404であって欲しい。以下のように記述することで実現できる。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader
from models import Article

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

Djangoでセッションを使う

ビュー関数の引数のrequestオブジェクトのsessionプロパティを使用する。ちなみにsessionを利用するには、MIDDLEWARE_CLASSESに「django.contrib.sessions.middleware.SessionMiddleware」が含まれている必要がある。

■取得

以下のようにしてセッションに格納した情報を取得することができる。

def get(request):
    data = request.session.get('key','')

第二引数はデフォルト値であり、省略することが可能である。

■保存

以下のようにしてセッションに値を保存することができる。

def set(request):
    request.session['key'] = [1, 2, 3]

デフォルトのセッション保存先はDBである。

Djangoで管理画面を作る

■設定ファイル編集

vi setting.py

以下のように記述する。

INSTALLED_APPS = (
    'dj.article',
    'django.contrib.admin',
)

以下のコマンドでDBを同期する。

./manage.py syncdb

■管理項目追加

以下のディレクトリにadmin.pyを追加する。

cd article
vi admin.py

admin.pyには以下のように記述する。

# -*- coding: utf-8 -*-
from django.contrib import admin
from dj.article.models import Article

admin.site.register(Article)

たったこれだけでarticleの追加ができるようになる。

■URL設定

vi urls.py

以下のように記述する。

from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^article/(?P<id>[0-9]+)/$', 'dj.article.views.article'),
)

以下のコマンドで開発サーバを起動してブラウザで/adminにアクセスする。

./manage.py runserver 192.168.1.123:8080

そう!管理画面はもうできてるんですな。

■つくり込む

dj/article/model.py

以下のように__unicode__メソッドを定義すると一覧の表示名がタイトルになる。

# -*- coding: utf-8 -*-
from django.db import models

# Create your models heire.
class Article(models.Model):
    id      = models.AutoField(primary_key = True)
    title   = models.CharField(max_length = 256)
    content = models.TextField()
    date    = models.DateField()

    def __unicode__(self):
        return self.title

    class Meta:
        db_table = 'article'

dj/article/admin.py

表示項目を増やしたい場合は以下のように記述する。

# -*- coding: utf-8 -*-
from django.contrib import admin
from dj.article.models import Article

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'date')# 表示項目

admin.site.register(Article, ArticleAdmin)