このエントリは、Bluemix(SoftLayer) Advent Calendar 2016の7日目の記事です。
#Vyatta(VyOS)とは
Bluemix Infrastructure(旧SoftLayer)で柔軟なネットワークを構築したいときに、ソフトウェアルータであるVyatta(VyOS)がよく利用されます。
ルーティングはもちろん、ステートフルファイアウォールやVPNなどの機能が使えます。
#Vyattaのコンフィグ
Vyattaの設定は、プロンプトから"show configuration"を実行するといわゆるrunning-configが表示されます。なんかJSONっぽい!
vyatta@vy01:~$ show configuration
firewall {
all-ping enable
broadcast-ping disable
config-trap disable
group {
address-group ipsec-peer {
・・・
}
}
}
interfaces {
・・・
}
・・・
完全にJSONだったら楽だったのですが、残念ながらJSONとも違う謎の記述法で書かれています。少なくとも木構造ではあるようです。
そこでこのコンフィグを扱いやすいデータ記述法に変換して、自動でExcelなどに貼り付けられたら、管理表が最新でないだの、実機と設定が違うだの、運用でありがちな問題から開放されるなーということで、変換ツールを作ってみます。開放されたい!
#コンフィグ変換の方針
変換するツールはどんなのが便利か考えてみました。
- 形式
- なんでもよいのですが、VBAでExcelに貼りたかったので今回はMSXMLなどパーサが充実しているXMLにします。
- ポータビリティ
- ツールは特定の環境に依存せず使えることが理想です。もっと言えばVyattaの上で実行できれば文句なしです。VyattaはDebianベースのLinuxなので、ある程度のコマンドが使えます。
- 環境を汚さない
- Vyattaの上で実行するとなれば、一時ファイルとかは残したくないです。あまりゴリゴリ重いこともやりたくないです。
上記方針を加味した結果、sedとtrだけでいこうとおもいます。
Vyatta上でも実行できるし、コンフィグを取ってきてローカルのcygwinやMacで実行してもよし。
やってみてだめだったら大人しくパールのようなものを使って書きます。(PythonはVyattaに入っていました。Rubyはなかった。)
XML化するにあたってルール決めが必要なところは以下のようにしました。
・要素名にハイフンは使わない
要素名にハイフンが含まれるとXMLが扱いにくくなる言語があるため、ハイフンはアンダースコアに変換します。
host-name vy01
↓
<host_name>vy01</host_name>
・名前っぽいところには属性を使う
ethernet eth1 {
address 172.16.0.1/24
}
↓
<ethernet name="eth1">
<address>172.16.0.1/24</address>
</ethernet>
・宣言だけのところは適宜単語を補う
rfc3768-compatibility
↓
<rfc3768_compatibility>true</rfc3768_compatibility>
#呪文
上記ルールに沿うようsedのコマンドを機械的に追加していった結果、よくわからないものが出来上がりました。真面目にPythonで書けばよかった!
"show configuration"の結果をパイプで食わせてあげればXMLが出てきます。
vyatta@vy01:~$ show configuration | 呪文
sed 's/enable-default-log/enable_default_log true/' |\
sed 's/rfc3768-compatibility/rfc3768_compatibility true/' |\
sed 's/repository supported/repository_supported/' |\
sed 's/local-zone$/local_zone true/' |\
sed 's/system$/forward_to system/' |\
sed 's@^\( *\)\([^ {}]\+\) \([^ {}]\+\)\( *\)$@\1<\2>\3</\2>\4@' | \
tr '\n' '~' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~*\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\( *\)}~@\1<\2></\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@\(~ *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
sed 's@^\( *\)\([^{}~]*\) {~\([^{}]*~\)\( *\)}~@\1<\2>~\3\4</\2>~@g' | \
tr '~' '\n' | \
sed 's@<address-group \([^>]\+\)>@<address_group name="\1">@' |\
sed 's@</address-group [^>]\+>@</address_group>@' |\
sed 's@<name \([^>]\+\)>@<rule_set name="\1">@' |\
sed 's@</name [^>]\+>@</rule_set>@' |\
sed 's@<rule \([^>]\+\)>@<rule priority="\1">@' |\
sed 's@</rule [^>]\+>@</rule>@' |\
sed 's@<bonding \([^>]\+\)>@<bonding name="\1">@' |\
sed 's@</bonding [^>]\+>@</bonding>@' |\
sed 's@<vif \([^>]\+\)>@<vif name="\1">@' |\
sed 's@</vif [^>]\+>@</vif>@' |\
sed 's@<vrrp-group \([^>]\+\)>@<vrrp_group name="\1">@' |\
sed 's@</vrrp-group [^>]\+>@</vrrp_group>@' |\
sed 's@<ethernet \([^>]\+\)>@<ethernet name="\1">@' |\
sed 's@</ethernet [^>]\+>@</ethernet>@' |\
sed 's@<loopback \([^>]\+\)>@<loopback name="\1">@' |\
sed 's@</loopback [^>]\+>@</loopback>@' |\
sed 's@<vti \([^>]\+\)>@<vti name="\1">@' |\
sed 's@</vti [^>]\+>@</vti>@' |\
sed 's@<interface-route \([^>]\+\)>@<interface_route network="\1">@' |\
sed 's@</interface-route [^>]\+>@</interface_route>@' |\
sed 's@<next-hop-interface \([^>]\+\)>@<next_hop_interface name="\1">@' |\
sed 's@</next-hop-interface [^>]\+>@</next_hop_interface>@' |\
sed 's@<route \([^>]\+\)>@<route network="\1">@' |\
sed 's@</route [^>]\+>@</route>@' |\
sed 's@<route6 \([^>]\+\)>@<route6 network="\1">@' |\
sed 's@</route6 [^>]\+>@</route6>@' |\
sed 's@<next-hop \([^>]\+\)>@<next_hop address="\1">@' |\
sed 's@</next-hop [^>]\+>@</next_hop>@' |\
sed 's@<remote-router \([^>]\+\)>@<remote_router address="\1">@' |\
sed 's@</remote-router [^>]\+>@</remote_router>@' |\
sed 's@<sync-map \([^>]\+\)>@<sync_map name="\1">@' |\
sed 's@</sync-map [^>]\+>@</sync_map>@' |\
sed 's@<community \([^>]\+\)>@<community name="\1">@' |\
sed 's@</community [^>]\+>@</community>@' |\
sed 's@<device \([^>]\+\)>@<device name="\1">@' |\
sed 's@</device [^>]\+>@</device>@' |\
sed 's@<user \([^>]\+\)>@<user name="\1">@' |\
sed 's@</user [^>]\+>@</user>@' |\
sed 's@<server \([^>]\+\)>@<server name="\1">@' |\
sed 's@</server [^>]\+>@</server>@' |\
sed 's@<repository \([^>]\+\)>@<repository name="\1">@' |\
sed 's@</repository [^>]\+>@</repository>@' |\
sed 's@<facility \([^>]\+\)>@<facility name="\1">@' |\
sed 's@</facility [^>]\+>@</facility>@' |\
sed 's@<esp-group \([^>]\+\)>@<esp_group name="\1">@' |\
sed 's@</esp-group [^>]\+>@</esp_group>@' |\
sed 's@<ike-group \([^>]\+\)>@<ike_group name="\1">@' |\
sed 's@</ike-group [^>]\+>@</ike_group>@' |\
sed 's@<proposal \([^>]\+\)>@<proposal number="\1">@' |\
sed 's@</proposal [^>]\+>@</proposal>@' |\
sed 's@<peer \([^>]\+\)>@<peer name="\1">@' |\
sed 's@</peer [^>]\+>@</peer>@' |\
sed 's@<zone \([^>]\+\)>@<zone name="\1">@' |\
sed 's@</zone [^>]\+>@</zone>@' |\
sed 's@<from \([^>]\+\)>@<from zone="\1">@' |\
sed 's@</from [^>]\+>@</from>@' |\
sed 's@<\([^-<>"]\+\)-\([^>]\+\)>@<\1_\2>@' |\
sed 's@</\([^-<>"]\+\)-\([^>]\+\)>@</\1_\2>@' |\
sed 's@<\([^-<>"]\+\)-\([^>]\+\)>@<\1_\2>@' |\
sed 's@</\([^-<>"]\+\)-\([^>]\+\)>@</\1_\2>@' |\
sed 's@<\([^-<>"]\+\)-\([^>]\+\)>@<\1_\2>@' |\
sed 's@</\([^-<>"]\+\)-\([^>]\+\)>@</\1_\2>@' |\
sed 's@^@ @' |\
sed '1i\<root>' |\
sed '$a\</root>'
Vyatta上にファイル置いちゃってもいいよという人は、スクリプトにして/tmpの下とかに置くといいとおもいます。
いろいろサニタイズして大幅に削っていますが、XMLは以下のようなものが出力されます。
<root>
<interfaces>
<ethernet name="eth0">
<address>192.168.0.1/24</address>
<duplex>auto</duplex>
<hw_id>XX:XX:XX:XX:XX:XX</hw_id>
<smp_affinity>auto</smp_affinity>
<speed>auto</speed>
<vrrp>
<vrrp_group name="1">
<advertise_interval>1</advertise_interval>
<preempt>true</preempt>
<priority>254</priority>
<rfc3768_compatibility>true</rfc3768_compatibility>
<sync_group>vgroup1</sync_group>
<virtual_address>192.168.0.3/24</virtual_address>
</vrrp_group>
</vrrp>
</ethernet>
<ethernet name="eth1">
<address>172.16.0.1/24</address>
<duplex>auto</duplex>
<hw_id>XX:XX:XX:XX:XX:XX</hw_id>
<smp_affinity>auto</smp_affinity>
<speed>auto</speed>
<vrrp>
<vrrp_group name="1">
<advertise_interval>1</advertise_interval>
<preempt>true</preempt>
<priority>254</priority>
<rfc3768_compatibility>true</rfc3768_compatibility>
<sync_group>vgroup1</sync_group>
<virtual_address>172.16.0.3/24</virtual_address>
</vrrp_group>
</vrrp>
</ethernet>
<loopback name="lo">
</loopback>
</interfaces>
<service>
<dns>
<forwarding>
<cache_size>150</cache_size>
<listen_on>eth1</listen_on>
<forward_to>system</forward_to>
</forwarding>
</dns>
<ssh>
<port>22</port>
</ssh>
</service>
<system>
<config_management>
<commit_revisions>20</commit_revisions>
</config_management>
<console>
<device name="ttyS0">
<speed>9600</speed>
</device>
</console>
<gateway_address>192.168.0.1</gateway_address>
<host_name>vy01</host_name>
<login>
<user name="vyatta">
<authentication>
<encrypted_password>****************</encrypted_password>
</authentication>
<level>admin</level>
</user>
</login>
<name_server>192.168.0.1</name_server>
<ntp>
<server name="ntp.nict.jp">
</server>
</ntp>
<package>
<auto_sync>1</auto_sync>
<repository_supported>
<components>main</components>
<distribution>stable</distribution>
<password>****************</password>
<url>http://packages.vyatta.com/vyatta_supported</url>
<username>""</username>
</repository_supported>
</package>
<syslog>
<global>
<facility name="all">
<level>notice</level>
</facility>
<facility name="protocols">
<level>debug</level>
</facility>
</global>
</syslog>
<time_zone>Asia/Tokyo</time_zone>
</system>
</root>
これを使ってVBAでExcelにぺたぺた貼ると、図のようなドキュメントが自動で作れました。便利。
よく設定変更するところしかテストできていないので、不足がいっぱいあると思います。基本的にsedの行を追加すれば対応できると思いますが、コメントなどで教えていただけますと幸いです。
VBAのコードは来年のアドベントカレンダーにでも。。