發表文章

目前顯示的是 一月, 2011的文章

unittest2.case.assertRaises 的實作方式

New and Improved Coming changes to unittest in Python 2.7 & 3.2 有詳細的 unittest2 介紹, 其中我覺得最漂亮的改進是 assertRaises, 用法變成:
with self.assertRaises(TypeError) as cm: do_something() exception = cm.exception self.assertEqual(exception.error_code, 3) 我覺得新介面用 with 執行, 既易懂又能彈性地執行不同行數的程式。想不到它怎麼做出這樣的行為。好奇之下, 看了一下原始碼, 才發覺作法很簡單:
def assertRaises(self, excClass, callableObj=None, *args, **kwargs): # ... if callableObj is None: return _AssertRaisesContext(excClass, self) # ... class _AssertRaisesContext(object): """A context manager used to implement TestCase.assertRaises* methods.""" def __init__(self, expected, test_case, expected_regexp=None): self.expected = expected self.failureException = test_case.failureException self.expected_regexp = expected_regexp def __enter__(self): return self def __exit__(self, exc_type, exc_value, tb): if exc_type is None: try: …

Django middleware 基本應用: 過濾黑名單

參考了幾份程式, 寫法大同小異, 這裡有簡單的 code snippet, 另外 django-ban 大小適中, 不會太複雜而不想看, 又附 sample project 說明怎麼設定, 若沒用過 middleware 的話, 可以參考看看。

概念就是自己寫個 middleware, 加入 settings 裡。middleware 有實作 process_request, 在函式裡檢查 REMOTE_ADDR (我看了好幾份程式都先優先用 HTTP_X_FORWARDED_FOR, 但這樣並不安全), 若在黑名單內, 就傳回 http.HttpResponseForbidden()。感覺這種寫法滿乾淨的。

剛開始一直在找類似 pre-processor 之類的語法, 因為學生時期玩 Rails 1.8 時, 好像有這樣的東西, 就「直覺」地找類似的東西。後來才想到, 這種要擋在全部連線之前的程式, 似乎正好適合寫在 middleware 裡。找對方向後, 就找到一堆東西了。

備註: 找的過程中看到有人用 ipcalc, 計算 ip subnet 的好東西, 之後需要再裝來用。

在 Django 發生 exception 時記錄相關訊息

官網文件可知, views 裡發生任何 exception 時, Django 會執行 handler500, 所以在 urls.py 裡設自己的 handler500 就可以攔截所有 500 server error, 接著就是如何取得 exception 內容。後來看到 ticket #4007 才知道可以從 sys.exc_info 裡取得相關資訊。後來想想, 這表示 logging.exception() 應該已包好對應的操作, 所以做法相當簡單:
改 urls.py 加入自己的 handler500在 handler500 裡先呼叫 logging.exception(MESSAGE)再讀入自己的 500.html, 回傳結果附帶一提, 404 的行為也值得一看, 可以參考內建的 404 handler 自己寫一份, 方便記錄查不到的頁面。官網建議改 template 就好, 不要自己寫 handler404, 不過我覺得複制程式碼, 指定到自己的 handler, 日後方便加東西。

其餘 views 在找不到頁面時, 直接 raise http.Http404(), 就會進入 handler404。當 settings.DEBUG = False 時, url routes 找不到對應的 url 時也會呼叫 handler404。

用 apache2 轉址

原本想用 virtualhost + script 轉址, 查了一下相關作法, 才發現用 mod_rewrite 簡單多了。懶得看 mod_rewrite 超詳細說明, 可以直接看 URL Rewriting Guide, 有常見問題的設法, 第二則就是轉址 (Canonical Hostnames)。附帶一提, RewriteCond 預設是和之後條件做 AND 運算, 可以下參數改用 OR。對照一下 Guide 再查查相關 directive 大概說明, 就知道怎麼用在自己的情況了。 2015/01/04 更新 注意設定放在 Directory 下會比較複雜, 直接放在最上層或 VirtualHost 可避免「未知」的錯誤, 見 Per-directory Rewrites

mercurial-server 運作流程

執行新版 1.1  後, 在 push 時會出現以下的錯誤訊息:
remote: error: changegroup.aaaaa_servelog hook raised an exception: 'str' object has no attribute 'format'弄清楚 mercurial-server 執行過程後, 發現這是因為 1.1 版用 python 2.6 的語法 (str.format) 造成的,  我是用 python 2.5。修改 /usr/local/share/mercurial-server/mercurialserver/servelog.py 去掉 format 和 json 相關的程式就好了。若不修正的話, /var/lib/mercurial-server/repos/REPO/.hg/mercurial-server.log 不會存有操作記錄。

解決問題後, 順便記一下 mercurial-server 的運作流程:
ssh -> /var/lib/mercurial-server/.ssh/authorized_keysauthorized_keys 裡記錄: no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command="/usr/local/share/mercurial-server/hg-ssh KEY_FILE SSH_PUBLIC_KEY" 藉由這裡的設定會啟動 hg-ssh 處理透過 ssh 來的指令。 hg-ssh 是個 python script, 會載入 package /usr/local/share/mercurial-server/mercurialserver/ 下的 modules 來完成指令mercurialserver.config 載入 /var/lib/mercurial-server/.mercurial-server/var/lib/mercurial-server/.mercurial-server 讀入權限設定、hg hooks 等從 hg hooks 設定 (/etc/mercurial-server/remote-hgrc.d/log…

顯示網路流量的指令 vnstat -l

上次要用時想不起來, 找了一陣子, 先記在這裡吧。

vnstat: 顯示歷史記錄vnstat -l: 顯示目前收發的流量vnstat -l -i eth1: 同上, 改顯示 eth1 的流量

REMOTE_ADDR vs. HTTP_X_FORWARDED_FOR

REMOTE_ADDR 是 apache2 設的變數, client 很難欺騙 apache, 但 HTTP_X_FORWARDED_FOR 是由 HTTP header 的 X-Forwarded-For 來的, client 可以自己設任意值。所以若要用 IP 做認證, 要用 REMOTE_ADDR。缺點就是使用者透過 proxy 連線就會將同一 proxy IP 的人一視同仁, 挺多再用 cookie 做區別。

幾篇參考資料:
使用HTTP_X_FORWARDED_FOR获取客户端IP的严重后果Security implications of Request.ServerVariables(“REMOTE_ADDR”) vs Request.ServerVariables(“HTTP_X_FORWARDED_FOR”)星空夜雨: 獲取用戶IP地址的三個屬性的區別(HTTP_X_FORWARDED_FOR,HTTP_VIA,REMOTE_ADDR)

安裝 mercurial server

筆記一下 mercurial server 安裝過程。

照著 README 所言打 sudo make setup-adduser, 結果出現錯誤, 看起來是缺一些 package 無法產生文件。反正用不到文件, 看了一下 Makefile, 找出必要部份, 一步步自己 make, 跳過產生文件那步 即可:
sudo make installetcsudo make pythoninstallsudo make addusersudo make initusermercurial server 認 public key 管權限, 看一下官網文件就會懂怎麼用。先將第一個使用者加入  root 權限:
sudo cp ~/.ssh/id_rsa.pub /etc/mercurial-server/keys/root/USERsudo -u hg /usr/local/share/mercurial-server/refresh-auth第二步會將 key 加入 hg 帳號的 authorized_keys 裡。用 USER 執行 ssh hg@localhost 後看到以下的訊息表示 key 有設好: mercurial-server: direct logins on the hg account prohibited
Connection to localhost closed.
有 root 後, 再來透過 repository hgadmin 來管比較方便, 也可以留 commit log:
hg clone ssh://hg@localhost/hgadmin ~/hgadmincd ~/hgadmin/mkdir -p keys/root/mkdir keys/root/USERcp PUBLIC_KEY_OF_USER_AT_HOST keys/root/USER/HOSThg add keys/root/USER/HOSThg ci -m 'Add USER@HOST with root permission'hg push push 後就會更新結果。若需要做細部的設定, 參考官網文件修改 /etc/mercurial-server/access.conf。

xargs + mv

之前一直很弱的只會用 xargs 加 rm、echo 這種不需另加參數的用法。剛需要用到 mv 就查了一下, 找到這篇, -n 和 -I 看起來不錯用。xargs + mv 的寫法:
$ ... | xargs -I @ mv @ DEST_DIR
另外附上一個用到 -n 的情境, 查詢 log 裡所有 IP 的 host name, 方便依 domain name 做些後處理:
$ awk '{print $1}' /var/log/apache2/access.log | sort -nu | xargs -n 1 host

找出目錄下非特定使用者擁有的檔案

find -uid 可以找目錄下特定使用者有的檔案, 反過來不知怎麼找。

今天靈機一動, 想到可以這麼搞, 不夠直接, 至少能用就是了:
ls -lR DIR | grep "^[-rw]\{10\} " | grep -v USER
2011-01-22 更新

DK 留言說加 "!" 即可, man page 沒看清楚, 唉呀呀:
find /path ! -uid 1234

在 Ubuntu 上編原始碼安裝的方法

官網有超詳細的說明, 看完這兩篇後就有基本戰鬥力了:
CompilingEasyHowTo: 超詳細說明, 文中提到幾個連結也值得一看CompilingSoftware: 沒上篇詳細, 不過有提到一些上篇沒提到的東西編原始碼的三個步驟是 ./configuremakemake install最難的是第一步, 這要看 tarball 裡附的 INSTALL 或 README 或 ./configure --help 了解相關設定, 要有耐心。編的過程遇到問題就用 apt-file 或 auto-apt 找出相關套件, 裝好後再繼續, 缺 header 的話, 通常是要裝 X-dev  這類 package。 第二步沒什麼好說的, 第三步最好改用 checkinstall, 可以包成 package 再裝, 日後可用 dpkg -r PKG 移除, 還有用 dpkg -l 查版本, 用 dpkg -L PKG 了解裝了那些檔案。以前用 make install 都會怕怕的, 不知道到底裝了什麼, 裝爛後要怎麼移除。有 checkinstall 實在太方便啦!! 若有多台同 OS 的機器, 編一次之後, 剩下的機器可以用 package 安裝, 省下不少工夫。 但是 Ubuntu 上用 checkinstall 安裝時會出現 "No such file or directory" 的錯誤, 從 console 的訊息來看, checkinstall 嘗試一些 mkdir 的操作, 卻沒有真的建出那些目錄。查了一下, 官網的 bug report 有提到解法, 要加 --fstrans=no, 也就是用 sudo checkinstall -D --fstrans=no 來安裝。另外可用 –install=no 只包 package 不安裝, 還有用 --requires 自己建 package dependency。這裡有些介紹, 超簡單的。

用 apt-file、auto-apt、wajig 從檔案找 package

Find which package contains a file in Debian Linux 提到各種從檔案找 package 的作法。試了幾個例子, 覺得 apt-file 找的東西最多, 找出來的雜訊自然也多。比較之後, 我還是會用 apt-file 吧。wajig 滿有趣的, 將相關工具包成一個 python script, 改天來讀讀原始碼, 應該可以學到一些東西。

dpkg 常用參數

記一下常用的幾個參數

dpkg -l : 列出所有裝過的 package 和版號, 裝過再移除也會留有記錄dpkg -L PKG: 列出 PKG 裝入的檔案dpkg -i PKG 或 dpkg -r PKG: 安裝或移除 PKG, 很少會用到這個, 大多用 aptitude

Ubuntu 上編 apache2 + mod_wsgi 原始碼以及 module、設定的簡介

基本安裝流程
sudo aptitude install auto-apt checkinstallsudo chown YOUR_ACCOUNT /usr/local/srccd /usr/local/srcwget http://ftp.stut.edu.tw/var/ftp/pub/OpenSource/apache//httpd/httpd-2.2.17.tar.gztar zxvf httpd-2.2.17.tar.gzcd httpd-2.2.17auto-apt run ./configure --enable-mods-shared=most --enable-so --enable-rewrite --enable-expires --enable-cache --enable-usertrackmakesudo checkinstall -D --fstrans=noln -s /usr/local/apache2/bin/apachectl /etc/init.d/apachectl將 /etc/init.d/apachectl 加到 /etc/rc.local 或用 udpate-rc.d 裝到 /etc/rc*.d/ 裡安裝和設定 mod_wsgi 參考官網 QuickInstallationGuide, 沒什麼阻礙就順利編好 啟動、停止等操作: 透過 apachectl 操作, 它是一個 shell script, 最新版的很精簡, 幫忙傳參數給 httpd附帶一提, 裝 Ubuntu package 的話, 操作方式變成 /etc/init.d/apache2 -> apache2ctl -> apache2, 多一層 /etc/init.d/apache2 另外包一些操作。httpd 或 apache2 只是編出來的名稱, 可用 --with-program-name 設定 安裝、設定的相關資訊 ./configure --help:看有那些參數可選, 都不選也還 OK 啦apache2 有兩種 MPM (Multi-Processing Modules) 模式: prefork (multiple process) 或 worker (multiple thread), 預設是 prefork。Python thread 有頂頂大…

rsync 壓縮參數的大概測試心得

昨天意外發現 rsync 有 -z 的參數, 可以壓縮再傳。好奇它的效果就試了一下, 結果得到出人意外的結論: 有時候直接全部重新複製還比較快........, 雖然是很明顯的事實, 用慣 rsync 後到沒想到這點。

簡易的測試環境如下:
原始檔案: 一堆目錄合起來 7G 左右放在 A 機器目標檔案: 另一台機器放著很久以前的版本, 6G 左右, 至少有 1G 的差異gigabyte 網路簡易的測試結果如下:

用 rsync -av --delete 跨機器傳8m加上 -z超過 13m, 我等不下去, 就停了用 rsync 在目的機器產生新目錄, 不 diff2m54s用 rsync -z 在目的機器產生新目錄, 不 diff超過 6m, 我等不下去, 就停了scp2m47s
懶得測傳說中最快的 nc + compression, 有興趣的人可以看看《High performance MySQL 2e》 Appendix A, 有多種不同組合的測試數據。
在傳輸過程中用 iostat -dx 觀察兩台機器的 utility rate 和用 htop 看 rsync 的 CPU 使用狀態, 就可以明白加 compression 應該不會有好下場。不加前就是 IO rate 低, CPU 100%; 加 -z 後變成 IO rate 更低, CPU 當然還是 100%。

Zombie process 的說明

發完上篇後, 看到 freedom 和 command 回說 Zombie 反而是最常見 process 殺不掉的情況。於是又複習了一下 Zombie 相關說明, 摘要一下:
process 結束後要等 parent process 讀取它的 exit status 才能結束, 在那之前, 會進入 Zombie 狀態 (ps 顯示 Z)。Zombie 不會占用 memory, 只是在 process table 裡留筆記錄, 所以挺多會用光 process id 而無法新增 process。Zombie 不是 process, 自然也不能接受 signal, 所以無法 kill zombie (geek 的雙關笑話嗎...)。避免 Zombie 的解法: 用 double fork, 也就是不直接將工作交給 child process, 而是先 fork child process, 讓它再 fork 出 "child child process"。接著 parent process 會 wait child process, 而 child process 不等待 "child child process" 直接結束, 所以 parent process 也不會等多久就能繼續做事。依 Unix 的設計, 因為 "child child process" 的 parent process 掛了, 它會自動被 init 接管, 而 init 會定期讀取 child process 的 exit status。另外就是 Wikipedia 上寫的, 我沒試過這招: if the parent explicitly ignores SIGCHLD by setting its handler to SIG_IGN (rather than simply ignoring the signal by default) or has the SA_NOCLDWAIT flag set, all child exit status information will be discarded and no zombie processes will be left. Btw, 用 Google 搜過去寫的 plurk, 還是…

NFS 造成殺不掉的 process

原因詳見這篇, 當一個 process 進入 uninterruptible sleep 時 (ps 的狀態顯示 D), 表示這個 process 不能被中斷, 所以即使用 kill -9 也沒用。一個 process 進入 uninterruptible sleep 時, 表示它正在等 I/O。所以, 唯一的解法是讓它等到 I/O 或是放棄等 I/O。若使用的裝置是外接式硬碟、光碟機之類的, 可以試著重接讓 process 離開狀態 D。若等待的裝置是 NFS 的話, 需要另外的解法。

NFS 官網的 D12 描述的解法如下:
Sometimes it can at a couple of interations of the "kill processes" then "umount -f" cycle until the filesystem is unmounted, but it usually works.If all else fails, you can still unmount the partition on which the processes are hanging using the "umount -l" command. This causes the stuck mount to become detached from the file system name space hierarchy on your client, and will thus no longer be visible to other processes結論是: 重覆 kill、umount -f、mount 多次應該會有效。再不行的話, 改試看看 umount -l。我自己試的時候是 「umount -l, mount, kill (失敗), umount -f」 結果就好了。

強迫 mysql 照指定的順序 join

語法:
SELECT STRAIGHT_JOIN ... FROM A, B, C ... 這樣就會照 table 出現的順序 (即 A, B, C) join。若你很清楚怎麼 join 最省事, 用 STRAIGHT_JOIN 可以避免 mysql 猜錯順序造成超大 join。

我遇到的情況是需要用 primary key 分批取出 A 部份 rows 和 B, C, D 做 join, 再做些計算。這樣資料不會過大而無法處理。但交給 mysql 自行判斷時, 因為資料分佈的變化, mysql 有時選擇先 join B, C, D 最後才 join A, 但 join B, C, D 會造成大量 IO, 速度極慢。反之, 先取 A 部份的 rows 再往後 join, 取得資料不多。

Ubuntu security update

找了一會兒, 只看官網的 AutomaticSecurityUpdates, 從 Ubuntu 討論區也只看到有人說執行 package update, 和 Stephon 確認也是這麼說, 沒想到 update package 就是 security update 啊。

官網連結附的指令可以比較「安全」地更新套件。更新完後記得檢查有無產生 /var/run/reboot-required, 有的話表示需要重開機。