僕とコードとブルーハワイ

omega (@equal_001) の日記

zencoding-vim から emmet-vim へ

リポジトリが移動してた。

GitHub - mattn/emmet-vim: emmet for vim: http://emmet.io/


そういえばzen-codingというPluginがemacsにあったなーVimのもあるだろうなと思って調べたらやっぱり存在して、NeoBundleしたのだけど、Vimを起動したところ以下のエラーが出た。

Unfortunately, zencoding-vim was moved to emmet-vim, please use it.

ということで、NeoBundle 'mattn/emmet-vim' し直したのだった。

python の doctest

26.3. doctest — 対話的な実行例をテストする — Python 3.5.1 ドキュメント

そういえば使ったことがなかったので触ってみたのと、使い道とかの個人的な感想メモ。

pythonでテストするときはunittest, pytest, pylintあたりしか使ったことがなかった。


demo.py

"""
>>> test("omega")
'Hello, omega!'
"""

def test(name):
    return "Hello, {}!".format(name)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

実行結果

~$ python demo.py -v  # -vすると実行詳細がみれる

"""
>>> test("omega")
'Hello, omega!'
"""

def test(name):
    return "Hello, {}!".format(name)

if __name__ == "__main__":
    import doctest
    doctest.testmod()
OMEGA:~$ python test.py
OMEGA:~$ python test.py -v
Trying:
    test("omega")
Expecting:
    'Hello, omega!'
ok
1 items had no tests:
    __main__.test
1 items passed all tests:
   1 tests in __main__
1 tests in 2 items.
1 passed and 0 failed.
Test passed.

こんな感じ。
ちなみにdocstringの気分で書いて、Expectingにシングルクォートで囲まないHello, omega!を記述していたら見事にfailedになった。

上記ではdemo,pyでdoctestをimportしてるけど、コマンドラインからdemo,pyを単体モジュールとしてimportしてテスト実行することもできる。

~$ python -m doctest -v demo.py

テストしたい内容が増えたりするとpythonファイルが肥大化するので、そういうときは testmod()の代わりにtestfile()と使うとよい。
doctest.testfile("test_list.txt") と書いてtest_list.txtの中にTrying/Expectingをガシガシ書いていけばいい。

test_list.txtの中身はこんな感じ。

>>> from demo import test
>>> test("omega")
'Hello, omega!'

なおこれもコマンドラインから実行できる。

~$ python -m doctest -v test_list.txt

例外について思ったところ

例外については公式ドキュメントにも書いてあった。
トレースバックを貼ればいいだけらしいけど、filepathとか変更されやすいものだったりrandom的なもので毎回値が変化するようなものには向いていない。
そもそもあまり例外でテストするのは良くないと思うけど。
例外に関して評価される部分としては、例外の型からそれ以降だけらしい。
以下の場合、最初の二行はスルーされる。

Traceback (most recent call last):
    ... 
ValueError: multi
    line
detail

やっぱり例外を扱うのはよろしくないと思う。なってほしい結果の一例としてのwikiのようなものくらいのほうが良いと思う。

単体テストAPI

多分使わないと思うけど、doctestをunittest.TestSuite に変換することができる機能を持っている。

デバッグ

使わないと思うけど使いそうなところを挙げるとpdbが使える、 TestCaseのdebug()が使える。以上。

感想

doctestはexampleとして記述しておくのが良いのかなと思った。私の中では今のところ、docstringをより良いものにするためのという位置付け。
あと、モジュールの方針・本当に実現したい機能を思い出させてくれるものとしても良いかも。
ガシガシコード書いてると、よく方向性見失ったりするので、、

ExDOS を動かしてみた

天才あらわる 14歳の少年プログラマが作った64ビットOS「ExDOS」が凄い という記事を見て、何やらOSを自作した人がいてそれが公開されているという情報を得たので実際に動かしてみた。

ダウンロード

ExDOS -- Downloads
ここからダウンロードしてくる。
現時点ではdemo状態のものだけが公開されている。
今回はdemo2.zipを手元に持ってきた。

ExDOSを動かす

ExDOS -- Tested Hardware を読むと、VirtualBox5.0上でならいい感じに動くぜ!と書いてたのでVirtualBox5.0上で動かした。
仮想ハードディスクを追加しない設定で作成して、ストレージからHDD選択でdemo2.vmdkを指定するだけ。

Runしたらこんな画面になった。
ウィンドウが二つ。こやつらはマウス操作で位置を動かせた。拡大縮小とかはできなかった。
f:id:equal_001:20160408020221p:plain

右下にDEBUGというボタンみたいなのあったのでクリックしたら、ターミナルっぽくなった。
helpと入力すると使えるコマンド一覧でてくる。
コマンドミスしてるのは、英数字切り替えボタンを押す癖が抜けなくて無意識に「 ` 」を入力していたというオチ。
f:id:equal_001:20160408020513p:plain

とりあえず手当たり次第にコマンド打って遊んだ。
無知すぎてportコマンド群の意味がよくわからなかった。
f:id:equal_001:20160408020549p:plain

感想

  • とにかく起動が早かった。まだそんなに機能ないからだとは思うけど、サクサク。
  • たまにカーソル操作がおかしくなって左側にぴょんと飛んだりするトリッキーな動きもして翻弄された。
  • ターミナルの履歴機能はまだなかったけど、ここまで作れた人にとってもそういう機能の導入って難しいのかな。(OS作ったことないのに生意気言ってごめんなさいでもあるとコマンドの動作確認とか便利だろうなって思ったんですごめんなさい)
  • まだドキュメントはcomming soonだったのでできるの楽しみ。


ソースコードはGihubで公開されている。
GitHub - omarrx024/exdos64: Extensible Disk Operating System (64-bit version)

アセンブラ力がゼロなので特になにも解読できませんでした。
でもこういうモジュール(と言っていいのかわからないけど)の切り方するんだなとか、kernel.asmを起点に眺めているとなんとなーくだけど、中でやってることがが見えてきた。
keyboard.asmにはhelpで出したコマンドの関数定義があったり(アセンブラは読めないけど)。

面白かったので真面目にUnixカーネルのコード読もうかなという気持ちにちょっとなった。

pyinvoke を触ってみた

pyinvoke

Welcome to Invoke! — Invoke documentation
pyinvokeは、いろんなコマンドをまるっと纏めることができるモジュール。
たとえば、開発で頻繁に必要となるコマンドを一つに集約させる場合にこのpyinvokeが生きてくる。
いま仕事でDjangoコマンド、テスト実行コマンド、環境毎の実行コマンドなどをpyinvokeでまとめているけどinvokeさえ覚えればいいのでとても楽。

今回の実行環境

OS Pythonバージョン invokeバージョン
MacOSX Yosemite 10.10.3 3.4.3 0.10.1(2016/03/30現在最新)

インストール

$ pip install invoke

使い方

Getting started — Invoke documentation に使用方法が書かれています。
基本は、tasks.pyに@taskを関数の上に宣言して実行させたい内容を関数内に記述するだけ。
また、関数Aの@task()の引数に関数Bを渡すと、関数Aを実行する前に関数Bが実行される(pre-taskの設定)。
試しに、invokeコマンドから「demo/demo.txtに任意の文字列を追記する」「demoディレクトリを削除」という2つのタスクを追加する。

tasks.py

import os
from invoke import task, run


@task
def mkdir_demo():
    """demoディレクトリがなかったら作成
    """
    if not os.path.exists("demo/"):
        os.mkdir("demo")

@task(mkdir_demo)
def add_text(line):
    """demo.txtに任意の文字列を追記する
    """
    run("echo {} >> demo/demo.txt".format(line))

@task
def reset():
    """demo/をまるっと削除
    """
    run("rm -r demo/")


invoke --listすると、追加したコマンドの一覧を表示できる。このとき、docstringが各コマンドの説明として出てくる。
ただ、pre-taskもこのリストに表示されるのが微妙。

$ invoke --list
Available tasks:

  add_text     demo.txtに任意の文字列を追記する
  mkdir_demo   demoディレクトリがなかったら作成
  reset        demo/をまるっと削除

実際にコマンドを実行してみる。

$ ls demo/
ls: demo/: No such file or directory  # demoディレクトリが存在しないことを確認

$ invoke add_text hello

$ cat demo/demo.txt
hello  # demoディレクトリが作成され、demo.txtに指定した文字列"hello"が追記されている

$ invoke add_text world!

$ cat demo/demo.txt

hello
world!  # 追記されている

Rundeck Config・ジョブ通知・Mysql の設定編

前回はRundeckをローカルで動かすまでのメモを書いた。
Rundeck 基礎編 インストールからジョブ実行までのメモ - 僕とコードとブルーハワイ

今回は、実際に使用するときに設定する色々なことをメモしていく。

環境

Ubuntu 14.04
Rundeckバージョン:2.6.2

Rundeckの設定

公式ページを見ながら設定していく。
Configuration

Config設定が置いてある場所と構成は以下の通り。
今回は最低限の設定だけする。いじるのはframework.properties、project.properties、profileくらい。

omega:~$ sudo tree /etc/rundeck/
/etc/rundeck/
├── admin.aclpolicy
├── apitoken.aclpolicy
├── framework.properties
├── jaas-loginmodule.conf
├── log4j.properties
├── profile
├── project.properties
├── realm.properties
├── rundeck-config.properties
└── ssl
    └── ssl.properties
framework.properties

Rundeckのコア部分の設定を定義するファイル。
ホスト名やBaseURL、Login User/Passwdなど。

# ----------------------------------------------------------------
# Rundeck server connection information
# ----------------------------------------------------------------

framework.server.name = rundeck-example.com
framework.server.hostname = rundeck-example.com
framework.server.port = 4440
framework.server.url = http://localhost:4440  # 自鯖内で経由するのでlocalhost
# Username/password used by CLI tools.
framework.server.username = omega
framework.server.password = agemo
                          :
rundeck-config.properties

データベース、logレベル、メール通知の設定などをするファイル。

#loglevel.default is the default log level for jobs: ERROR,WARN,INFO,VERBOSE,DEBUG
loglevel.default=INFO
rdeck.base=/etc/rundeck

#rss.enabled if set to true enables RSS feeds that are public (non-authenticated)
rss.enabled=false
grails.serverURL=http://rundeck-example.com
dataSource.dbCreate=update
dataSource.url=jdbc:h2:file:/var/lib/rundeck/data/rundeckdb;MVCC=true;TRACE_LEVEL_FILE=4

上記の設定をしてRundeckを再起動すると、設定ファイルが反映される。
http://rundeck-example.com にアクセスしてログイン画面になったらOK。

ジョブ通知の設定

Rundeckではジョブのステータス(Success, Failed, Start)で通知を飛ばせる仕組みがある。
今回はSlack通知と Email通知の設定方法を書いていく。

Slackに通知を飛ばす

前回のRundeckメモに書いたけどもう一回書く。
準備はrundeck-slack-incoming-webhook-pluginの公式ページからrundeck-slack-incoming-webhook-plugin-0.5.jar(この記事を書いた時点での最新版) をダウンロードしてきて、/var/lib/rundeck/libext/配下に設置して、Rundeckを再起動するだけ。

$ sudo mv rundeck-slack-incoming-webhook-plugin-0.5.jar /var/lib/rundeck/libext/

再起動後にWebUIからジョブの作成ページを見てみると、Send Notification?の欄にSlack Incoming WebHookが追加されている。
f:id:equal_001:20160326183007p:plain

SlackのIncoming WebHooksからWebhook URLを取得してフォームに入力してあげるだけでOK。

簡単なジョブを実行して、Slackへ実行結果が送信されていることを確認。
実行結果ページへのリンクも貼られているのも便利。

f:id:equal_001:20160326182711p:plain

メールで通知する

Email Settings によると、SSL越しに接続したりと高度な設定をしたい場合はrundeck-config.propertiesではなくrundeck-config.groovyに記述しましょうとのこと。
/etc/rundeck/にrundeck-config.groovyを作成する。
今回はGmailSMTPを使う(楽だから)。
中身は以下のように書くかんじ。

loglevel.default="INFO"
rdeck.base="/var/lib/rundeck"
rss.enabled="false"

grails {
        serverURL="http://rundeck-example.com"
        mail {
                host="smtp.gmail.com"
                port = 465
                username="example@gmail.com"
                password="example"
                props = ["mail.smtp.auth":"true",
                      "mail.smtp.socketFactory.port":"465",
                      "mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
                      "mail.smtp.socketFactory.fallback":"false"]
        }
}
grails.mail.default.from = "example@gmail.com"

dataSource.url = "jdbc:h2:file:/var/lib/rundeck/data/rundeckdb;MVCC=true;TRACE_LEVEL_FILE=4"
dataSource.dbCreate = "update"
rundeck.projectsStorageType = "db"

Goovyファイルを書くのが初めてだったのでたまに文法エラーで時間食った。ドキュメントをよく読もう。
Groovy Language Documentation

デフォルトではrundeck-config.propertiesを読み込むようになっているので、rundeck-config.groovyを読み込ませるように以下の1行を/etc/rundeck/profileへ追記する。

RDECK_JVM='$RDECK_JVM -Drundeck.config.location=/etc/rundeck/rundeck-config.groovy'"

Rundekを再起動後、WebUIからジョブの作成ページのSend Notification?をYesにして、Send EmailのTo、Subjectを入力すればメール通知の設定は完了。
Attach output logを選択すると実行の過程で出力されたlogのtxtファイルがメールに添付されてくる。
f:id:equal_001:20160326185835p:plain

Mysqlを使用するための設定

デフォルトでは H2 Database が使用される。
公式ページにMysqlを使用するときの設定方法が書かれていたのでメモ。

MysqlにRundeckで使用するデータベースを作成する

mysql> create database rundeckdb;
Query OK, 1 row affected (0.00 sec)
mysql> grant ALL on rundeck.* to 'rundeckuser'@'localhost' identified by 'rundeckpassword';
Query OK, 1 row affected (0.00 sec)

rundeck-config.groovyにMysql接続のための設定を追記する

以下のように書く。

    :
dataSource.driverClassName="com.mysql.jdbc.Driver"
dataSource.url="jdbc:mysql://example.com/rundeckdb?autoReconnect=true"
dataSource.username="rundeckuser"
dataSource.password="rundeckpassword"
    :

mysql connector について

新しいバージョンのRundeckには デフォルトで/var/lib/rundeck/exp/webapp/WEB-INF/lib配下にmysql-connectorが置いてあるけど、もしMysqlがうまく動作しなかったりmysql-connectorが無い/デフォルトバージョンだと動かない場合は、MySQL :: Download Connector/J から mysql-connector-java-5.x.x-bin.jarをダウンロードして /var/lib/rundeck/server/lib/配下に設置するとよい。


上記の設定をしてRundeckを再起動したら設定完了。

テーブルはこんなかんじ。

mysql> show tables;
+----------------------------+
| Tables_in_rundeckdb        |
+----------------------------+
| auth_token                 |
| base_report                |
| execution                  |
| log_file_storage_request   |
| node_filter                |
| notification               |
| orchestrator               |
| plugin_meta                |
| project                    |
| rdoption                   |
| rdoption_values            |
| rduser                     |
| report_filter              |
| scheduled_execution        |
| scheduled_execution_filter |
| storage                    |
| workflow                   |
| workflow_step              |
| workflow_workflow_step     |
+----------------------------+
19 rows in set (0.00 sec)

mysqldumpしてプロジェクトとかジョブの情報を取っておくとよい。
でもresources.xmlの情報はこの中に入ってないので別途バックアップを取っておく必要がある。

Rundeck 基礎編 インストールからジョブ実行までのメモ

最近触る機会があったので、その記録として導入方法から少しずつ使用方法をまとめていく。

Rundeckとは

とても雑にいうとcron機能の進化版みたいなもの。(とか言ったら各所から怒られそう)
時間毎にジョブを実行させるのはもちろん、複数サーバへのパラレル実行、WebUI操作、外部サービスへの通知連携など、多機能なサービスです。


インストール

環境:Ubuntu 14.04
Rundeckバージョン:2.6.2


公式サイト( Rundeck.org - Downloads )にOSごとのパッケージ取得方法が書かれているので、これを参考に取得する。

$ apt-get install openjdk-7-jdk 
$ dpkg -i rundeck-2.6.2-GA.deb


上記でローカルでRundeckを使う環境が整うので、早速Rundeckを起動。

$ service rundeckd start


localhost:8000にアクセスすると以下のページが表示される。この画面が表示されれば正常に起動している。
デフォルトはadmin:admin
ユーザとパスワードの設定は後述する設定ファイルで変更可能。

f:id:equal_001:20160326023136p:plain

プロジェクト作成

ログイン後、以下の画面に遷移するのでNew Project+を押す。
f:id:equal_001:20160326023132p:plain

New Project+を押すと、プロジェクトのConfig画面に遷移するので、Project Nameを入力する。秘密鍵を登録済みなら、Default Node Executor のSSH、 Default Node File CopierのSCP(画像では切れてるがSSHの下にある) を設定する。
f:id:equal_001:20160326023538p:plain

ジョブの作成

プロジェクトを作成したらプロジェクト一覧画面に遷移するので、New Jobを押す。
f:id:equal_001:20160326024317p:plain
ジョブの設定画面では最低限以下の項目を入力する。

  • Job Name: ジョブの名前。どういうコマンドを実行させるジョブかわかる名前をつける。
  • Description: このジョブの説明を書く。
  • Workflow: コマンドまたはスクリプト実行を設定する。Commandはワンラインコマンド、ScriptはShellスクリプトを登録できる。このプロジェクトではScriptを使用している。

今回はhelloといいう文字列を出力するhello.pyを実行するジョブを作成する。
f:id:equal_001:20160326025340p:plain


ローカルで実行するなら必要はないが、リモートサーバでジョブを実行させる場合は以下項目の設定が必要となる。

  • Nodes: 実行するリモート環境を選択する。リモート環境を選択するには、/var/rundeck/projects/${project.name}/etc/resources.xml を編集する必要がある。(Webからは設定できないので、Rundeckサーバに入って直接編集する)

f:id:equal_001:20160326030542p:plain

resources.xml の設定
  • node nage: ジョブ作成ページの Nodes のNode Filterで入力する名前。
  • hostname: リモート先のホスト名
  • username: リモート先に入るときのユーザ名

以下の例だと、ジョブ作成時にNode Filterでexampleと入力して設定しておくことで、プロジェクト作成時に設定した秘密鍵を使用してRundeckがSSHしてジョブを実行する。ssh -i id_rsa example-user@example.com というようにしているのと同じ。

<?xml version="1.0" encoding="UTF-8"?>

<project>
  <node name="OMEGA.local"
    description="Rundeck server node"
    tags=""
    hostname="OMEGA.local"
    osArch="amd64"
    osFamily="unix"
    osName="Linux"
    osVersion="3.13.0-76-generic"
    username="omega"/>
  <node name="example"
    description="example node"
    tags=""
    hostname="example.com"
    osArch="amd64"
    osFamily="unix"
    osName="Linux"
    osVersion="3.13.0-76-generic"
    username="example-user"/>
</project>

その他、オプションで細かな設定ができる。よく使用するものを挙げる。

  • Thread Count: 並列数の選択。
  • Send Notification: ジョブ結果の通知設定。通知するタイミングは成功・失敗・スタートの3種類選べる。通知方法はEmail、Webhook(Slackも可能)の方法を選べる。

Schedule to run repeatedly: ジョブの実行周期を設定する。

  • Log level: 実行結果のログレベルの設定。Nomalならコマンド結果の標準出力、Debugなら- - - Rundeckが実行しているより詳細なログを出力する。Debugにすると、実行終了までの時間が多少遅くなる。

ジョブの実行

Saveボタンを押してジョブを保存すると、登録したジョブの管理画面になる。
ジョブを実行するには、Run job Now を押す。

f:id:equal_001:20160326031116p:plain

実行状況を確認する管理画面に遷移する。Log Outputタブを押してみると、「hello.pyの実行」ジョブのタスクによりhelloという表示がされている。成功。
f:id:equal_001:20160326031438p:plain

秘密鍵の登録方法

2通りの登録方法がある。

WebUIからの秘密鍵を登録する

Rundeck全体の管理画面に遷移して、Key Storageを選択、Add or Upload a Key を押して鍵を登録する。

f:id:equal_001:20160326032010p:plain

任意の場所に鍵を置いてresources.xmlでPATH指定して読み込む

所定のディレクトリに設置した秘密鍵を、以下の例のようにresources.xml でPATH定義して読み込める。
鍵のパーミッションの設定を忘れずに。
パーミッション・・・.ssh 700、rundeck-key 600 chown rundeck:rundeck

例)秘密鍵が/etc/rundeck/.ssh/rundeck-keyにある場合
  <node name="example"
    description="example node"
    tags=""
    hostname="example.com"
    osArch="amd64"
    osFamily="unix"
    osName="Linux"
    osVersion="3.13.0-76-generic"
    ssh-authentication="privateKey"
    ssh-keypath="/etc/rundeck/.ssh/rundeck-key"
    username="example-user"/>

こっちの方法でやったほうが色々楽だと思う。
また別に書くけど、AnsibleでRundeck環境を諸々構築するときに鍵を配置できるので、基本WebUIからの登録はしない。

slackへ通知するプラグインの追加

デフォルトでは使用できないが便利なプラグインを公開してくれている方がいるので、それを利用させて頂く。
使用方法も以下のページに書いてるのでここを読めばOK。
github.com

rundeck-slack-incoming-webhook-plugin-0.5.jar をダウンロードしてきて、/var/lib/rundeck/libext/配下に設置するだけ。

$ sudo mv rundeck-slack-incoming-webhook-plugin-0.5.jar /var/lib/rundeck/libext/

S3へファイルを並列アップロードする

S3へ大量かつ大きいファイルをアップロードするときには xargs が使える


例)
/tmp/uploads/ にあるファイルを並列(プロセス10)でアップロードする

$ ls /tmp/uploads/ | xargs -L1 -P10 aws s3 cp /tmp/uploads/ s3://bucketname --recursive

参考: Man page of XARGS