トップ 追記

本 日 の h o g e

hogeとはワイルドカードのようなものです。日々起こった、さまざまなこと −すなわちワイルドカード− を取り上げて日記を書く、という意味で名付けたのかというとそうでもありません。適当に決めたらこんな理由が浮かんできました。

更新情報の取得には rdflirs を使ってもらえると嬉しいです.


11/18/2017 ふむ [長年日記]

tDiary 5504日目

[Py][小ネタ] SQLAlchemy でグラフ

相変わらず長いこと放置しているので,秋の夜長にと言うには少し遅いけど,何となく遊び.

ノードに種類を持たせつつ,多対多でノード同士を繋いでみたりするなど.

import uuid
import json

from contextlib import contextmanager

from sqlalchemy import create_engine
from sqlalchemy import Table, Column, ForeignKey, String
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
Engine = create_engine('sqlite:///:memory:')
Session = sessionmaker(bind=Engine)

NodeAssoc = Table(
    'node_assoc', Base.metadata,
    Column('parent_id', String(36),
           ForeignKey('node.id', onupdate='CASCADE', ondelete='CASCADE'),
           nullable=False),
    Column('child_id', String(36),
           ForeignKey('node.id', onupdate='CASCADE', ondelete='CASCADE'),
           nullable=False),
)


@contextmanager
def txn():
    session = Session()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()


class ModelBase:
    def __init__(self, **kwargs):
        self.id = str(uuid.uuid4())
        self.apply(**kwargs)

    def apply(self, **kwargs):
        for key, val in kwargs.items():
            setattr(self, key, val)

    def dictify(self, ignore=['id']):
        return {n: getattr(self, n)
                for n in self.__table__.columns.keys() if n not in ignore}

    def __repr__(self):
        return json.dumps(self.dictify())


class Node(ModelBase, Base):
    TYPE = None

    id = Column(String(36), primary_key=True)
    type = Column(String(255), nullable=False)
    value = Column(String(255))
    children = relationship(
        'Node', secondary=NodeAssoc,
        primaryjoin=id == NodeAssoc.columns.parent_id,
        secondaryjoin=id == NodeAssoc.columns.child_id,
        backref='parents', uselist=True)

    __tablename__ = 'node'
    __mapper_args__ = {'polymorphic_on': type}

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.type = self.TYPE


class CircleNode(Node):
    TYPE = 'circle'
    __mapper_args__ = {'polymorphic_identity': TYPE}


class SquareNode(Node):
    TYPE = 'square'
    __mapper_args__ = {'polymorphic_identity': TYPE}


class TriangleNode(Node):
    TYPE = 'triangle'
    __mapper_args__ = {'polymorphic_identity': TYPE}


if __name__ == '__main__':
    def dump(mode, nodes):
        def _dump(mode, node, level=1):
            print(' ' * level, node)
            for n in getattr(node, mode):
                _dump(mode, n, level + 2)

        print(mode)
        for node in nodes:
            _dump(mode, node)
        print()

    Base.metadata.create_all(bind=Engine)

    with txn() as session:
        t1 = TriangleNode(value='1')
        t2 = TriangleNode(value='2')
        t3 = TriangleNode(value='3')
        s1 = SquareNode(value='1', children=[t1, t2])
        s2 = SquareNode(value='2', children=[t2, t3])
        c1 = CircleNode(value='1', children=[s1, s2])
        session.add(c1)

    with txn() as session:
        for mode, cls in [('children', CircleNode), ('parents', TriangleNode)]:
            dump(mode, session.query(cls).all())

実行するとこうなる.

children
  {"type": "circle", "value": "1"}
    {"type": "square", "value": "1"}
      {"type": "triangle", "value": "1"}
      {"type": "triangle", "value": "2"}
    {"type": "square", "value": "2"}
      {"type": "triangle", "value": "2"}
      {"type": "triangle", "value": "3"}

parents
  {"type": "triangle", "value": "1"}
    {"type": "square", "value": "1"}
      {"type": "circle", "value": "1"}
  {"type": "triangle", "value": "2"}
    {"type": "square", "value": "2"}
      {"type": "circle", "value": "1"}
    {"type": "square", "value": "1"}
      {"type": "circle", "value": "1"}
  {"type": "triangle", "value": "3"}
    {"type": "square", "value": "2"}
      {"type": "circle", "value": "1"}

だからどうしたぼくドラえもん.

本日のツッコミ(全3件) [ツッコミを入れる]

mpet [クワッ!!って、来たら、ササッとやってスッ…て、避けたら、おぉっ! って、感じですな。 これはこれで、趣がありま..]

mpet [ちなみに近況報告。 atzmもっさり元気してる? こっちは、もっさもっさしてる。安定のいっぱいいっぱいさ..]

atzm [あけおめ. 明鏡止水の心持ちにて真を見れば,風呂の物体もホットプレートの浮島も泡沫夢幻の如く盛者必衰の理を表して候..]


05/28/2017 ふむ [長年日記]

tDiary 5330日目

[Py][小ネタ] lesync

exFAT なパーティションにディレクトリツリーを同期したいことがあるのだが,

  • 10 年以上前からデスクトップのファイルシステムが EUC-JP で,convert するのも面倒なので放置している
  • しかし exFAT はファイル名が UTF-8 じゃないと書き込みを拒否するくさい

といったようなことがあって,面倒臭いなあと思っていた.

ファイルの同期といえば rsync(1) 大先生なのだが,単に -a オプションを使うと chown(2) でエラーを吐くとかまあ色々あって,概ね下記のようなオプションを指定することになる.

# rsync -rpt --iconv eucjp,utf8 SRC DST

とはいえ覚えられないし打つのも面倒だし,alias するとか wrapper 書くとかすることになると思うのだが,どうせ rsync(1) ほど高機能/高性能なものを求めているわけでもなし,せっかくなのでフルスクラッチで書いてみた. それが lesync

とはいえどうせなら zero-copy でいっちょやってみるかと,データ本体を user space にメモリコピーすることはないようにした. その結果が下記.

$ sudo bash -c 'echo 1 > /proc/sys/vm/drop_caches'
$ time sudo lesync -S hoge /mnt/card/tmp/.
real    0m3.328s
user    0m0.467s
sys     0m0.347s

$ rm -rf /mnt/card/tmp/hoge
$ sudo bash -c 'echo 1 > /proc/sys/vm/drop_caches'
$ time sudo rsync -rpt --iconv eucjp,utf8 hoge /mnt/card/tmp/.
real    0m3.633s
user    0m1.296s
sys     0m0.304s

rsync(1) はデータを user space に持ってきてから kernel space に送る (read(2) したデータを write(2) する) という動作をするので,zero-copy にすることで user 時間に差が現れる. まあ,real は大差ないので大した意味はないけれど.

lesync はデフォルトでは差分をチェックせず cp -r のような動作をするので,更新されたファイルだけコピーしたい場合は上記のように -S オプションを指定する. 差分のチェックはデフォルトでサイズ,mtime,ファイル名の比較だけど,プラットフォームが Linux Kernel Crypto API をサポートしていれば md5 等のハッシュ値を比較することもできる. 例えば sha1 ハッシュ値を比較したい場合は下記のようにする.

$ sudo lesync -S -D sha1 hoge /mnt/card/tmp/.

hashlib を使わなかったのは,言わずもがな zero-copy を堅持するため. とはいえ如何に zero-copy と言えど,ハッシュ値の比較を使うとめちゃくちゃ遅くなるので,使いどころはあまりないのだけれど.

ちなみにハッシュ値を算出する部分は独立したモジュールに分けておいたので,下記のようにハッシュ値を求めることができる.

$ sudo bash -c 'echo 1 > /proc/sys/vm/drop_caches'
$ time python3 -m lehash -a sha1 data
440a94499f1862fab752280ab7c4470b7d859a69  data
real    0m4.592s
user    0m0.071s
sys     0m2.279s

$ sudo bash -c 'echo 1 > /proc/sys/vm/drop_caches'
$ time sha1sum data
440a94499f1862fab752280ab7c4470b7d859a69  data
real    0m4.393s
user    0m2.332s
sys     0m0.272s

zero-copy のため user 時間と sys 時間に差が生まれているのが見て取れるけど,こちらもまあ real は大差ないので大した意味はない.


05/20/2016 ふむ [長年日記]

tDiary 4957日目

[日記] ロマサガ 2

ついカッとなって PS Vita 版ロマサガ 2 を買ってしまったので,どうせなら 4000 年プレイでもするかーとちまちまプレイ.ようやく完走したので記録しておく.

1011年  第二皇子皇帝 ジェラール        術研究所建設
1505年  サイゴ族皇帝 ハールファグル    カンバーランドを帝国領にする
1756年  ホーリーオーダー皇帝 ピーター  アバロン帝国大学建設
2008年  人形皇帝 コッペリア            ヴィクトール運河奪回
2258年  ハンター皇帝 バイ              ヴィクトール運河にレオンブリッジ建設
3010年  イーリス皇帝 クラウディア      ボクオーンを倒し 地上戦艦を撃沈
3260年  ノーマッド皇帝 ベスマ          七英雄のリーダー ワグナスを浮遊城にて撃破
3510年  陰陽師皇帝 ドウマン            サイゴ族の子供を助け ダンターグ出現 これを撃破
3761年  海女皇帝 ナタリー              氷海においてスービエを撃破
4013年  最終皇帝                       アバロン新市街建設
4014年  最終皇帝                       封印の地にてクジンシーを再び撃破
4016年  最終皇帝                       サラマットを解放しロックブーケを撃破
4018年  最終皇帝                       さまよえる湖にてノエルを打倒
4019年  最終皇帝                       ラストダンジョンにてスービエを撃破
4019年  最終皇帝                       七英雄との戦いにピリオドを打つ!

陣形ロスが何よりイヤなので最終皇帝前に陣形持ちクラスを皇帝にすべく各所を制圧している. これにより 2 年のロスがあるはずなので,おそらく PS Vita 版の最大は 4021 年だろうか. ちなみにヒラガは 99 世,トーマは 80 世だった.

なお可哀想なスービエはいつも二度死ぬ.


01/06/2016 [長年日記]

tDiary 4822日目

[Gentoo] Gentoo (OpenRC) で DHCPv6 client

ふと気付くと,DHCPv6 ready な環境であるはずなのに,手元のマシンに IPv6 アドレスが (linklocal 以外) 付与されていなかった. 何か特殊な設定必要だったっけなーと思って色々調べてみるも,どうにも何だか要領を得ない記述しか見当たらない.

仕方がないので,面倒だけど netifrc を改造して DHCPv6 client ready にした. 改造と言ってもインストール済みファイルを手で書き換えてパッケージシステムを否定するような悪行には手を染めたくないので,epatch_user を使って対処した.

# mkdir -p /etc/portage/patches/net-misc/netifrc-0.2.2
# cd /etc/portage/patches/net-misc/netifrc-0.2.2
# wget http://www.atzm.org/depot/netifrc-0.2.2-dhclientv6.patch
# emerge net-misc/dhcp net-misc/netifrc

こんな感じで,DHCPv6 client ready な netifrc をインストールすることが出来る. ただし DHCPv6 client は optional としてあるので,例えば下記のように設定を追加する必要がある. なお手元のマシンでは IPv6 アドレスを付与したい I/F がブリッジだったので,I/F 名は br0 になっている.

modules_br0="dhclient dhclientv6"
config_br0=("dhcp" "dhcpv6")
dhcpv6_br0="nodns nontp accept_ra autoconf"

modules_br0 や config_br0 は見たまんまなので割愛するとして,dhcpv6_br0 について.

nodns, nontp:
これらは素の dhclient でも指定可能なオプションで,例えば nodns は,/etc/resolv.conf に手を付けてくれるなよ,という指示を行うもの. v4, v6 で /etc/resolv.conf 等の上書き合戦を行われるのは気持ちが悪いので,v6 の方では明示的に切っている. 気にしない人は別に指定せずとも良い.
accept_ra, autoconf:
何かよく分からんのだけど,どうも OpenRC が強制的にブリッジインタフェースの net.ipv6.conf.br0.accept_ra とか net.ipv6.conf.br0.autoconf とかを勝手に無効化してくれやがるので,これらを指定することで有効化できるようにした. 対象がブリッジでなければたぶん必要ない.

ちなみに config_br0 が array になっているので OpenRC に毎度怒られることになるのだけれど,だからと言って dhcp と dhcpv6 をスペース区切りの文字列で書いても機能してくれなかった. しかし怒られることに快感を覚える諸兄にはむしろご褒美だと思うので,もはや深くは考えていない.

とか何とか書いた矢先に

dhclientv6: Add DHCPv6 support via dhclient という commit を見つけた.

けど 2013 年の commit が stable になってないってどういうことなの...


12/15/2015 ふむ [長年日記]

tDiary 4800日目

[Py][OpenFlow] Trema day #8 で発表してきた

あまりにも放置しすぎているので,たまには何か書こうか... ということで.

ひょんなことからお声かけ頂いて,先日 Trema day #8 で発表してきた. ネタは OpenFlow 環境での L2 traceroute について. 資料は slideshare に置いておいた ので興味のある方はどうぞ. コードも oftroute, oftgui に公開しているので,virtualenv でも作って

$ pip install git+https://github.com/atzm/oftroute.git
$ pip install git+https://github.com/atzm/oftgui.git

とか何とかやればインストールできると思います. コマンドとか使い方は簡単にドキュメントに書いてあるけど,基本的にはコントローラを起動して mininet とかを繋いでやれば動作を見るくらいはできる. Ryu アプリとして書いてあるので,ライブラリ的に (?) 扱って他の Ryu アプリと連携して動くこともできるはず.

L2 の traceroute と言えば IEEE 802.1ag とか Ethernet OAM とかまあそんなキーワードで語られることが多いというかそれがスタンダードなので当然なんだけども,さてそれがそのまま OpenFlow 環境下で使えるかというと,必ずしもそうとは限らない.

例えば IEEE 802.1ag では OAM フレームを Ethertype 0x8902 でぶん投げることによってその機能を果たすわけだけれども,OpenFlow Switch がそれをそのまま吸い上げてしまうようなことをすると,制御対象外ネットワークから入ってくる OAM フレームまで吸い上げてしまうことになるので,場合によっては好ましくないこともある.

また OpenFlow 環境では,普通の IEEE 802.1D 的な Learning Switch と違って,主に L2,3,4 のフィールドを使って転送経路を自由に設定できてしまうので,例えば ARP は経路 A を通るけど,IPv4 は経路 B を通る,というようなネットワークを組めてしまう. こうなると,特定のフレームが通る経路だけでなく,「どういうフレームだったら」どういう経路を通るのか,ということを確認する術が欲しくなる.

スライドにも書いてある通り,この手の話についてはいくつも論文が発表されているようで,今さら何か目新しいネタというわけでもなさそうなのだけれど,(公開されてるものだけだけど) 読んでみると頭の弱い自分にはどうにも難しすぎる... ということで,自分でも分かる/作れるくらいに簡素化/最適化したやり方をひねり出してみたのでした.

あと 1 つ注意点. 一応 traceroute 実行時にフレーム転送時間が出るようになってるけども,現状これはとても信用ならない値なので無視した方が良い. というのも,Ryu の (というか Ryu が内部で使ってる tinyrpc の?) WebSocket ライブラリの都合で,client が ack を返さないと server はそこで block してしまうらしい. 要するに,client (oft-traceroute コマンドや oftgui Web 画面) が server (oft-controller とか) に ack を返すまでの時間が,転送時間に乗ってきてしまっているということ. これはどうしたもんかなぁ... と思いつつ,今に至る...

何はともあれ,関係者の皆様,参加の皆様,お疲れ様でした & ありがとうございました.