Puppet設定ガイドメモ
これのこと
Configuring Puppet — Documentation — Puppet Labs
Configuration Guide
初回設定の方法が書かれている
設定ファイル
- メイン設定ファイル: /etc/puppet.conf
- yumとかpackage baseでインストールしたなら、自動で作られてるはず
設定できるパラメータとデフォルト値を見るには、下記のコマンドを実行すると表示される
$ /usr/bin/puppet --genconfig
DNSの設定
- puppetクライアントはサーバ名:puppetを参照する
- localのDNSを構築しているなら、サーバにCNAMEを設定したほうがよい
puppet IN CNAME crabcake.picnic.edu.
CNAMEを指定すれば、いちいちそれぞれのクライアントでサーバを指定しなくても済む
CNAMEレコードの設定は、DNS and Bind本を参考にするといいだろう
- CNAMEレコードを追加後、nameサーバをリスタート(非DynamicDNSの場合)
DynamicDNSなら、下記のようにコマンドで設定(再起動不要)
$ nsupdate -k <tsig_key_file> -d > server ns.picnic.edu > zone picnic.edu > update add puppet.picnic.edu. 3600 CNAME crabcake.picnic.edu. > send
- サーバとクライアントの両方の/etc/hostsにホストエントリを追加することも可能
For the server:
127.0.0.1 localhost.localdomain localhost puppet
For the clients:
192.168.1.67 crabcake.picnic.edu crabcake puppet
WARNING: もし、puppetに対してpingできても、クライアントからサーバに接続できないというログが出力されていたら、サーバでport 8140が開放されているか確認する
Puppet Lauguage設定
Site Manifest作成
- Puppetは、宣言型システム
- puppet独自コードで記述されているmanifestを使う
- サイト全体の設定を中心に管理するのは、site manifest
- manifestは、複数に分割してもよい
- nodeごとの個々のシステムの差異は取り除かれるべき
- Puppetは、Primaryのmanifestとして/etc/puppet/manifests/site.ppを使う
- /etc/puppet/manifestsを作成して、そこにmanifestを追加する
- manifestの変更trackのために、scm(git, svnとか)を使うことを強く推奨
Manifestの例
とっかかりとして、sudoersファイルの適切なパーミッションを保持する例
# site.pp file { "/etc/sudoers": owner => root, group => root, mode => 440 }
Start the Central Daemon
- 多くは一つのサーバでいいはず
- Puppet Labsが公開しているドキュメントにスケールアウトとフェールオーバーについてのベストプラクティスがあるので、必要に応じて追加すればいい
- サーバの決定: puppetmasterが動作
- puppetmasterはinitスクリプトで自動起動した方がよさげ
- 必要に応じて、daemonで使うpuppetユーザとグループを作成する
daemon起動時に作成する場合
# /usr/sbin/puppetmasterd --mkusers
- puppet daemonを起動すれば、必要な証明書、ディレクトリ、ファイルは自動的に作成される
インストールの確認
- Puppetは、クライアントをサーバに対してすべてが正常に動作するか確認を行うことができる
- 最初のクライアントは、--waitforcertフラグを有効にすべき(default: 120秒)
# puppetd --server myserver.domain.com --waitforcert 60 --test
- --testフラグは、フォアグラウンドで出力を表示でき、一回のみ動作する
クライアント動作中のメッセージ
info: Requesting certificate warning: peer certificate won't be verified in this SSL session notice: Did not receive certificate
- 自動署名でなければ、上のメッセージが60秒おきにくりかえされる?
- 署名待ちリストをサーバで確認する方法
# puppetca --list
- クライアント名が表示されるはず
- 手動で署名
# puppetca --sign mytestclient.domain.com
※ waitforcertはdefault:5分らしいが、CentOS5.4環境でrpmインストールしたpuppetでは2分になってる
GitのTagへ署名してみる
はじめに
今まで、ある程度まとまった機能が開発できたら、タグ付けするということを繰り返してきましたが、
今まで使用していたのは、軽量版のタグだということに気がつきちょっと調べて見ると、タグには注釈を付けることができ、さらにそれに対して署名が可能だということがわかりました。
これは、開発者であるコミッターとリリースタグを付けるリリースマネージャみたいな異なる人がいる場合に有効な仕組みだと思います。
作成した注釈+署名付きタグを、git showコマンドで確認すると、コミットの情報の上に、タグの情報と署名が表示されます。
{タグ作成者の情報} {注釈メッセージ} {署名} {コミットの情報}
環境
MacOSX v10.6
GnuPGやGitは導入済みとします。
前準備
Gitに署名用のGPG秘密鍵を登録します。$HOME/.gitconfigに直接記述することもできるのですが、コマンドで登録した方が、Syntaxエラー等を防げるのでコマンドで登録します。
$ git config --global user.signingkey 1234FEDC
署名付きのタグを作成
下記のコマンドで署名付きタグを作成できます。(既に作成済みのタグへ署名する場合は、-fオプションを追加します)
$ git tag -s v0.0.2 -m 'Interactive shell support' 次のユーザーの秘密鍵のロックを解除するには パスフレーズがいります:“Gonbe Nanashi (hoge) <hoge@gmail.com>” 2048ビットRSA鍵, ID 1234FEDC作成日付は2010-09-23 Updated tag 'v0.0.2' (was 4c5f5b8)
署名されたか確認します。
いつものgit showコマンドを実行すると、GPG署名が一緒に表示されます。
$ git show v0.0.2 tag v0.0.2 Tagger: Gonbe Nanashi <hoge@gmail.com> Date: Thu Sep 23 18:58:42 2010 +0900 Interactive shell support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (Darwin) iQEcBAABCAAGBQJMmyTSAAoJEFWFhZJ7f/uc/AQIAJDjM/PiwixNgIrtFIuTglzM pG6YVFML5O63Rf1bmX8W9WAY7jWDufHZ5q+dY9PnzffM3Tz8HTiJMzKGjg66cZ89 vmuvaJ5nM/1hq1fbXdAjpXXxVPin/6jscpAjBefSQFckegXUCKe5Sl8K1M2c0ohY Qe7hViY9BlJXfn/xFgGBO9oWDcpz+hCcsb0SjyrsAirtPvX5axJEW/Eq3yKijXM4 QiR+q0B7/Sz9ymZJUB6wWEK/pdhf6w64pN+xEvaIHmrsqoPG3kXzhYOYCxQgeAms lEyd5caIKteKzQqAejtWW2y0Q4W7v+aeWr7lJfwMym+7f6iHwqsjVp87lzUaC+M= =QFab -----END PGP SIGNATURE----- commit e57c1135027592d6f25a444fe04b2aff8ebf0c39 Author: Gonbe Nanashi <hoge@gmail.com> Date: Thu Sep 23 17:13:19 2010 +0900 Add running interactive shell argument and parameters diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e92f57 --- /dev/null +++ b/.gitignore (...)
タグの検証
署名者の公開鍵を使用して、検証できます。
$ git tag -v v0.0.2 object e57c1135027592d6f25a444fe04b2aff8ebf0c39 type commit tag v0.0.2 tagger Gonbe Nanashi <hoge@gmail.com> 1285235922 +0900 Interactive shell support gpg: 木 9/23 18:58:42 2010 JSTにRSA鍵ID 1234FEDCで施された署名 gpg: “Gonbe Nanashi (hoge) <hoge@gmail.com>”からの正しい署名
参考
おまけ
タグをgithub等のリモートサーバへ送る
git push origin masterでやってるように、git push origin v0.0.2って実行してやるだけです。
$ git push origin v0.0.2
複数のタグを一度にpushしたい場合は、下記のコマンドを実行するといいらしいです。
$ git push origin --tags
リモートリポジトリからタグ一覧を取得する
まず、下記のコマンドで、自分のmasterブランチに最新リビジョンをマージします。
$ git pull origin master
タグはダウンロードされないので、下記のコマンドでタグを取得します。
$ git fetch origin --tags
最新のタグから、tarballを作成する
$ git describe v0.0.2 $ APP_VER=$(git describe) $ git archive --prefix=nanashi-${APP_VER#v}/ $APP_VER | gzip >nanashi-${APP_VER#v}.tar.gz $ tar tvf ~/src/nanashi-0.0.2.tar.gz -rw-rw-r-- 0 root root 5 9 23 23:41 nanashi-0.0.2/.gitignore -rw-rw-r-- 0 root root 0 9 23 23:41 nanashi-0.0.2/LICENSE -rw-rw-r-- 0 root root 27 9 23 23:41 nanashi-0.0.2/README -rwxrwxr-x 0 root root 4779 9 23 23:41 nanashi-0.0.2/nanashiCommand.py
ビルド用のディレクトリにtarballを送ってRPMを作成するとか、Webアプリだったら配信用ディレクトリに送ってrsync叩くとか。これで簡単にリリース管理ができますね。
GNU Privacy Guard(GPG)鍵を作ってみた
はじめに
GNU Privacy Guard(GPG)は、データの暗号化と署名に使用します。
使用例としては、下記のものがあります。
- 暗号化メール
- 電子署名
- RPMパッケージの署名
- Git Tagの署名
かつて、Gmailでのメール暗号化のため、Firegpgとともに導入しようとしましたが、日本語が文字化けする・ブラウザが突然落ちるという問題で断念していましたが、今回、GitのTag署名のため、MacOSXで再挑戦したため、その記録を残しておきます。
鍵生成
$ gpg --gen-key gpg (GnuPG) 1.4.10; Copyright (C) 2008 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. gpg: ディレクトリー「/Users/hoge/.gnupg」ができました gpg: 新しい構成ファイル「/Users/hoge/.gnupg/gpg.conf」ができました gpg: 警告: 「/Users/hoge/.gnupg/gpg.conf」のオプションは起動している間、有効になりません gpg: 鍵輪「/Users/hoge/.gnupg/secring.gpg」ができました gpg: 鍵輪「/Users/hoge/.gnupg/pubring.gpg」ができました ご希望の鍵の種類を選択してください: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (署名のみ) (4) RSA (署名のみ) 選択は? 1 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 要求された鍵長は2048ビット 鍵の有効期限を指定してください。 0 = 鍵は無期限 <n> = 鍵は n 日間で満了 <n>w = 鍵は n 週間で満了 <n>m = 鍵は n か月間で満了 <n>y = 鍵は n 年間で満了 鍵の有効期間は? (0) Key does not expire at all これで正しいですか? (y/N) y あなたの鍵を同定するためにユーザーIDが必要です。 このソフトは本名、コメント、電子メール・アドレスから 次の書式でユーザーIDを構成します: "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>" 本名: Gonbe Nanashi 電子メール・アドレス: hoge@gmail.com コメント: gpg key 次のユーザーIDを選択しました: “Gonbe Nanashi (hoge) <hoge@gmail.com>” 名前(N)、コメント(C)、電子メール(E)の変更、またはOK(O)か終了(Q)? O 秘密鍵を保護するためにパスフレーズがいります。 今から長い乱数を生成します。キーボードを打つとか、マウスを動かす とか、ディスクにアクセスするとかの他のことをすると、乱数生成子で 乱雑さの大きないい乱数を生成しやすくなるので、お勧めいたします。 +++++ ....+++++ 今から長い乱数を生成します。キーボードを打つとか、マウスを動かす とか、ディスクにアクセスするとかの他のことをすると、乱数生成子で 乱雑さの大きないい乱数を生成しやすくなるので、お勧めいたします。 +++++ .......+++++ gpg: /Users/hoge/.gnupg/trustdb.gpg: 信用データベースができました gpg: 鍵1234FEDCを絶対的に信用するよう記録しました 公開鍵と秘密鍵を作成し、署名しました。 gpg: 信用データベースの検査 gpg: 最小の「ある程度の信用」3、最小の「全面的信用」1、PGP信用モデル gpg: 深さ: 0 有効性: 1 署名: 0 信用: 0-, 0q, 0n, 0m, 0f, 1u pub 2048R/1234FEDC 2010-09-23 指紋 = A1C4 89E8 47BD E506 15BF B6DA 5585 8592 7B7F FB9C uid Gonbe Nanashi (hoge) <hoge@gmail.com> sub 2048R/ABCD9876 2010-09-23
公開鍵サーバへ自分の公開鍵を登録
下記のサイトにて、公開鍵の登録と検索が可能ですが、今回はコマンドラインから公開鍵サーバへ鍵を登録してみます。
Public Key Server Commands
公開鍵の送信
$ gpg --keyserver pgp.nic.ad.jp --send-keys 1234FEDC gpg: 鍵1234FEDCをhkpサーバーpgp.nic.ad.jpへ送信
公開鍵の検索と取得をするには、下記のようにします。
公開鍵の検索
$ gpg --keyserver pgp.nic.ad.jp --search-keys hoge gpg: “hoge”をhkpサーバーpgp.nic.ad.jpから検索 (1) Gonbe Nanashi (hoge) <hoge@gmail.com> 2048 bit RSA key 1234FEDC, 作成: 2010-09-23 番号(s)、N)次、またはQ)中止を入力してください >1 #<=番号を入力すると該当の公開鍵を入手できる gpg: 鍵1234FEDCをhkpからサーバーpgp.nic.ad.jpに要求 gpg: 鍵1234FEDC:“Gonbe Nanashi (hoge) <hoge@gmail.com>”変更なし gpg: 処理数の合計: 1 gpg: 変更なし: 1
これで、RPMパッケージの署名とか、GitのTag署名とかできますね。また、他の人の公開鍵を使って署名を検証するとか、暗号化メッセージを複合化するとかできますよね。(なお、他人の公開鍵を使用する場合、警告が出ますが、ローカルの信用データベースの更新とかの追加のタスクを実施することにより出なくなります。)
MacOSXにGnu indentをインストール
はじめに
前提
ローカルの環境
- UTF8
- タブ幅は4
- タブはスペースに変換しない
調査
MacOSX標準でインストールされているindentコマンドは、manを見る限りBSD由来のものでオプションにK&Rスタイルが用意されていないことがわかった。
$ man indent INDENT(1) BSD General Commands Manual INDENT(1) NAME indent -- indent and format C program source SYNOPSIS indent [input_file [output_file]] [-bacc | -nbacc] [-bad | -nbad] [-bap | -nbap] [-bbb | -nbbb] [-bc | -nbc] [-bl] [-br] [-cn] [-cdn] [-cdb | -ncdb] [-ce | -nce] [-cin] [-clin] [-dn] [-din] [-fc1 | -nfc1] [-in] [-ip | -nip] [-ln] [-lcn] [-lp | -nlp] [-npro] [-pcs | -npcs] [-psl | -npsl] [-sc | -nsc] [-sob | -nsob] [-st] [-troff] [-v | -nv] (...) HISTORY The indent command appeared in 4.2BSD. BUGS indent has even more switches than ls(1). A common mistake that often causes grief is typing: indent *.c to the shell in an attempt to indent all the C programs in a directory. This is probably a bug, not a feature. BSD June 29, 2004 BSD
何となく、MacPortsでindentを探してみる
$ port search indent indent @2.2.10 (devel) C language source code formatting program $ port info indent indent @2.2.10 (devel) Variants: universal Description: GNU indent changes the appearance of a C program by inserting or deleting whitespace according to a plethora of options. Some canned styles of formatting are supported as well. GNU indent is a descendant of BSD indent. GNU indent does NOT work for C++, only C. Homepage: http://www.gnu.org/software/indent/ Library Dependencies: gettext, libiconv Platforms: darwin License: unknown Maintainers: nomaintainer@macports.org
インストール
Gnu indentなら間違いなくK&Rスタイルは用意されているはずと思ってインストール
port install indent
インストールされたものがどこにあるかは下記のコマンドで確認できる
$ port contents indent Port indent contains: /opt/local/bin/gnuindent /opt/local/bin/gnutexinfo2man /opt/local/share/doc/indent/indent.html /opt/local/share/info/indent.info /opt/local/share/locale/ca/LC_MESSAGES/indent.mo /opt/local/share/locale/da/LC_MESSAGES/indent.mo /opt/local/share/locale/de/LC_MESSAGES/indent.mo /opt/local/share/locale/eo/LC_MESSAGES/indent.mo /opt/local/share/locale/et/LC_MESSAGES/indent.mo /opt/local/share/locale/fi/LC_MESSAGES/indent.mo /opt/local/share/locale/fr/LC_MESSAGES/indent.mo /opt/local/share/locale/gl/LC_MESSAGES/indent.mo /opt/local/share/locale/hu/LC_MESSAGES/indent.mo /opt/local/share/locale/it/LC_MESSAGES/indent.mo /opt/local/share/locale/ja/LC_MESSAGES/indent.mo /opt/local/share/locale/ko/LC_MESSAGES/indent.mo /opt/local/share/locale/nl/LC_MESSAGES/indent.mo /opt/local/share/locale/pl/LC_MESSAGES/indent.mo /opt/local/share/locale/pt_BR/LC_MESSAGES/indent.mo /opt/local/share/locale/ru/LC_MESSAGES/indent.mo /opt/local/share/locale/sk/LC_MESSAGES/indent.mo /opt/local/share/locale/sv/LC_MESSAGES/indent.mo /opt/local/share/locale/tr/LC_MESSAGES/indent.mo /opt/local/share/locale/zh_TW.Big5/LC_MESSAGES/indent.mo /opt/local/share/man/man1/gnuindent.1.gz
MacPortsでインストールされたコマンドはgnuindentなので、manでオプションを確認してみる
$ man gnuindent INDENT(1L) INDENT(1L) NAME indent - changes the appearance of a C program by inserting or deleting whitespace. SYNOPSIS indent [options] [input-files] indent [options] [single-input-file] [-o output-file] indent --version (...) -ipn, --parameter-indentationn Indent parameter types in old-style function definitions by n spaces. See INDENTATION. -kr, --k-and-r-style #<= 目的のオプションはこれ Use Kernighan & Ritchie coding style. See COMMON STYLES. -ln, --line-lengthn Set maximum line length for non-comment lines to n. See BREAKING LONG LINES. (...)
試す
下記にこれはひどいというソースコードを用意してみました。
#include <stdio.h> #include <stdlib.h> typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } Week_t; typedef struct { int year; int month; int day; Week_t w; } Date_t; void setdate(Date_t * dt, int y, int m,int d) { dt->year=y, dt->month = m, dt->day =d; } Week_t dayofweek(int y, int m,int d) { if(m <3) { y--; m+=12; } return (Week_t)((y + y/4-y/100+y/400+(13*m+8)/5+d)%7); } int main(int argc,char** argv) { int i =0; Date_t dt[3]; int ymd[][3] = { {2010,1,1,}, {1999,12,31,}, {2004,8,1,}, }; char wstr[][12] = { "Sunday","Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; for (i=0; i<3;i++) setdate(&dt[i], ymd[i][0], ymd[i][1], ymd[i][2]); for(i=0;i<3;i++) dt[i].w =dayofweek(dt[i].year,dt[i].month, dt[i].day); i=0; do { printf("It's %s (%d.%d.%d).\n", wstr[dt[i].w], dt[i].year, dt[i].month,dt[i].day); i++; } while (i<3); return EXIT_SUCCESS; }
こんなひどいソースコードを、Gnu indentはいとも簡単に読みやすくフォーマットしてくれます。
K&Rスタイルにフォーマット
$ gnuindent -kr -ts4 hoge.c #<= K&Rスタイルで、タブ幅4を指定
#include <stdio.h> #include <stdlib.h> typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } Week_t; typedef struct { int year; int month; int day; Week_t w; } Date_t; void setdate(Date_t * dt, int y, int m, int d) { dt->year = y, dt->month = m, dt->day = d; } Week_t dayofweek(int y, int m, int d) { if (m < 3) { y--; m += 12; } return (Week_t) ((y + y / 4 - y / 100 + y / 400 + (13 * m + 8) / 5 + d) % 7); } int main(int argc, char **argv) { int i = 0; Date_t dt[3]; int ymd[][3] = { {2010, 1, 1,}, {1999, 12, 31,}, {2004, 8, 1,}, }; char wstr[][12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; for (i = 0; i < 3; i++) setdate(&dt[i], ymd[i][0], ymd[i][1], ymd[i][2]); for (i = 0; i < 3; i++) dt[i].w = dayofweek(dt[i].year, dt[i].month, dt[i].day); i = 0; do { printf("It's %s (%d.%d.%d).\n", wstr[dt[i].w], dt[i].year, dt[i].month, dt[i].day); i++; } while (i < 3); return EXIT_SUCCESS; }
自分でコードをできるだけ素早く書きたいという時でも、indentコマンドが威力を発揮します。
2つのリストからdictionaryを作る
超個人的メモ
ループを使用しなくても作れるのか...
下記のようにしたい場合
list1 = [key1, key2, ..., keyN]
list2 = [value1, value2, ..., valueN]
↓
dict = {key1: value1, key2: value2, ..., keyN: valueN}
$ipython-2.6 [~/work] Python 2.6.5 (r265:79063, Jul 24 2010, 18:10:03) Type "copyright", "credits" or "license" for more information. IPython 0.10 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object'. ?object also works, ?? prints more. In [1]: In [2]: questions = ['name', 'quest', 'favorite color'] In [3]: answers = ['lancelot', 'the holy grail', 'blue'] In [4]: dict(zip(questions,answers)) Out[4]: {'favorite color': 'blue', 'name': 'lancelot', 'quest': 'the holy grail'}
FreeDNSに登録しているDNSレコードを更新するDynamicDNSクライアントを作成_Second
はじめに
DynamicDNSサービスに元々MyDNS.JPを使用していましたが、なぜか1レコード分以外のレコードがTXTレコードになってしまい、外部から自宅サーバへアクセスできなくなっていたので、FreeDNSに乗り換えてみました。
追記:エキスパートPythonとPython v2.6.2のマニュアルにも目を通し、おかしなところとか、Pythonは、GIL(Global Intepreter Lock)の問題とかで、同時実行できるスレッドが一つに限定されてしまうので、マルチスレッドの代わりに、マルチプロセスをよく利用するとなっているので、それも変更しました。
またDNS更新クライアントとして使用していた機器が故障し、普通のPCから更新することにしました。
手順
前提
DynamicDNSクライアント環境
sheevaplug(電源プラグ型LinuxPC: FlashROM)常時電源ONにするとデーモンがダウンしたりするため除外- DesktopPC(2002年くらいのノーブランド)
- Ubuntu10.04
- python v2.6
XML API取得
下記のURLにアクセスすると、XML形式でレコード更新用URLが取得できます。
(shaの部分は、登録ユーザ名+パスワードを使用してSHA-1で暗号化した文字列が入ります。)
URL: http://freedns.afraid.org/api/?action=getdyndns&sha=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&style=xml
SHA-1暗号化
暗号化文字列は、username + pipe + passwordをSHA-1で暗号化すればよいようなので、
下記のような関数で暗号化文字列を作成します。
def gethashstr(username, password): import hashlib hashobj = hashlib.sha1() encstr = '|'.join([username, password]) hashobj.update(encstr) hashstring = hashobj.hexdigest() return hashstring
更新用URLの取得
下記のように必要なパラメータを用意して
HASH_ARGO = "sha" TARGET_PARAMS = {'action': "getdyndns", HASH_ARGO: None, 'style': "xml"} TARGET_API = "http://freedns.afraid.org/api/" TIMEOUT = 300
urllib2を使ってDNSレコード更新用URLが記述されているXMLを取得しました。
def request(url, timeout, **params): query = '' for k, v in params.items(): query += '?' if query == '' else '&' query += '{key}={value}'.format(key=k, value=v) url += query import urllib2 print 'Connecting URL: {url} ...'.format(url=url) res = urllib2.urlopen(url=url, timeout=timeout) print 'URL: {url} contents download finished.'.format(url=url) responsetext = res.read() return responsetext TARGET_PARAMS[HASH_ARGO] = gethashstr(hoge, hogepass) response = request(TARGET_API, TIMEOUT, TARGET_PARAMS)
XMLからレコード更新用URL抽出
responsetextに取得したXMLが格納されているとして、XMLから更新用URLを抽出します。
XMLの解析モジュールは、dom,saxとかいろいろあったのですが、xml.etree.ElementTreeが一番簡単でしたので、それを使用しました。
利点
欠点
- XML文字列をそのまま渡すと例外が発生する(ファイルオブジェクトを渡す必要あり)
- しかし、StringIOで渡せば問題なく対処できる。
- 通常のPCサーバであればXMLをtempfileにリダイレクトして渡せばよいと思います。
<root> <item> <host>www.hoge.com</host> <address>71.1.1.1</address> <url>http://freedns.afraid.org/dynamic/update.php?T3faGkljdax==</url> </item> <item> {...} <url>http://freedns.afraid.org/dynamic/update.php?jklIIUadaaa==</url> </item> </root>
def update(responsetext, timeout): from xml.etree.ElementTree import ElementTree from StringIO import StringIO try: # xml.etree.ElementTree requires FileObject output = StringIO(responsetext) tree = ElementTree() tree.parse(output) urls = tree.findall("item/url")
DNSレコード更新
XMLから抽出した更新用URLにアクセスするだけでレコードの更新ができます。
for url in urls: res = urllib2.urlopen(url)
あとは、これをcrontabに1時間に1回動作するように設定しておけばいいのではないかと。
$ crontab -l
0 * * * * python /var/cron/freedns/freednsupdate.py
追記:
マルチプロセスを扱うワーカークラスを作成
#!/usr/bin/env python # -*= coding:utf-8 -*- from multiprocessing import Process import urllib2 class AsyncWorker(Process): def __init__(self, url, timeout): Process.__init__(self) self.url = url self.timeout = timeout def run(self): try: print 'Connecting URL: {url} ...'.format(url=self.url) res = urllib2.urlopen(url=self.url, timeout=self.timeout) except Exception, e: raise e else: print 'URL: {url} update successfully.'.format(url=self.url) return True
ワーカーを使用して処理を行う関数
# Update the dynamic dns records using API def update(responsetext, timeout): from xml.etree.ElementTree import ElementTree from cStringIO import StringIO try: # xml.etree.ElementTree requires FileObject output = StringIO(responsetext) tree = ElementTree() tree.parse(output) urls = tree.findall("item/url") # Record update from worker import AsyncWorker tasks = [AsyncWorker(u.text, timeout) for u in urls] for t in tasks: t.start() # wait for the background tasks to finish for t in tasks: t.join() print 'Dynamic dns update tasks has finished.' except Exception, e: raise e finally: output.close()
リスト内包表記とか使用しているものの、ワーカーの書き方とかSJC-P(Java)とかで勉強したものそのままひっぱてきた表現になってしまいました。
設定ファイルを読み込むクラスのメソッドの返却値も自分自身(self)を返却するようにして、メソッドチェーンっぽく続けて処理できるようにしてみました。
FreeDNSに登録しているDNSレコードを更新するDynamicDNSクライアントを作成
はじめに
DynamicDNSサービスに元々MyDNS.JPを使用していましたが、なぜか1レコード分以外のレコードがTXTレコードになってしまい、外部から自宅サーバへアクセスできなくなっていたので、FreeDNSに乗り換えてみました。
手順
前提
DynamicDNSクライアント環境
- sheevaplug(電源プラグ型LinuxPC: FlashROM)
- Ubuntu9.04
- python v2.6
XML API取得
下記のURLにアクセスすると、XML形式でレコード更新用URLが取得できます。
(shaの部分は、登録ユーザ名+パスワードを使用してSHA-1で暗号化した文字列が入ります。)
URL: http://freedns.afraid.org/api/?action=getdyndns&sha=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&style=xml
SHA-1暗号化
暗号化文字列は、username + pipe + passwordをSHA-1で暗号化すればよいようなので、
下記のような関数で暗号化文字列を作成します。
def gethashstr(username, password): import hashlib hashobj = hashlib.sha1() encstr = '|'.join([username, password]) hashobj.update(encstr) hashstring = hashobj.hexdigest() return hashstring
更新用URLの取得
下記のように必要なパラメータを用意して
HASH_ARGO = "sha" TARGET_PARAMS = {'action': "getdyndns", HASH_ARGO: None, 'style': "xml"} TARGET_API = "http://freedns.afraid.org/api/" TIMEOUT = 300
urllib2を使ってDNSレコード更新用URLが記述されているXMLを取得しました。
def request(url, timeout, **params): query = '' for k, v in params.items(): query += '?' if query == '' else '&' query += '{key}={value}'.format(key=k, value=v) url += query import urllib2 print 'Connecting URL: {url} ...'.format(url=url) res = urllib2.urlopen(url=url, timeout=timeout) print 'URL: {url} contents download finished.'.format(url=url) responsetext = res.read() return responsetext TARGET_PARAMS[HASH_ARGO] = gethashstr(hoge, hogepass) response = request(TARGET_API, TIMEOUT, TARGET_PARAMS)
XMLからレコード更新用URL抽出
responsetextに取得したXMLが格納されているとして、XMLから更新用URLを抽出します。
XMLの解析モジュールは、dom,saxとかいろいろあったのですが、xml.etree.ElementTreeが一番簡単でしたので、それを使用しました。
利点
欠点
- XML文字列をそのまま渡すと例外が発生する(ファイルオブジェクトを渡す必要あり)
- しかし、StringIOで渡せば問題なく対処できる。
- 通常のPCサーバであればXMLをtempfileにリダイレクトして渡せばよいと思います。
<root> <item> <host>www.hoge.com</host> <address>71.1.1.1</address> <url>http://freedns.afraid.org/dynamic/update.php?T3faGkljdax==</url> </item> <item> {...} <url>http://freedns.afraid.org/dynamic/update.php?jklIIUadaaa==</url> </item> </root>
def update(responsetext, timeout): from xml.etree.ElementTree import ElementTree from StringIO import StringIO try: # xml.etree.ElementTree requires FileObject output = StringIO(responsetext) tree = ElementTree() tree.parse(output) urls = tree.findall("item/url")