Ryu SDN FrameworkのBGP機能を試してみた(2) 〜InterAS接続編 その1〜
前回は、BGP/MPLS VPN網のエッジルータ適用でのコントロールプレーンの挙動を確認しました。しかし、実際にエッジルータ適用を想定した場合には、OSPFルーティングドメインへの参加や、トンネルMPLSラベル制御を含めたデータプレーン連携の実装が必要不可欠となります。現段階では、Ryu BGP実装状況での対応は困難そうです。よって、別の方式を検討してみます。
今回は、Ryu SDN FrameworkのBGPルーティング機能(以下、Ryu BGP)として、「InterAS MPLS VPN」による相互接続形態を検証してみます。
◆InterAS MPLS VPNとは ...
もともと、BGP/MPLS VPN網でのバックボーン内でのテナント分離手法として、「mp-BGP」が使われております。
BGP/MPLS VPN網との相互接続の場合にも、「mp-BGP」を使って、VPNv4アドレスファミリーを前提としたテナント分離情報を共有することが可能となります。なお、InterAS MPLS VPN相互接続には、複数の形態が選択可能です。rfc 4364によると、option a)〜c)までの3種類が規定されているようです。
a) VRF-to-VRF connections at the AS (Autonomous System) border routers.
b) EBGP redistribution of labeled VPN-IPv4 routes from AS to neighboring AS.
c) Multi-hop EBGP redistribution of labeled VPN-IPv4 routes between source and destination ASes, with EBGP redistribution of labeled IPv4 routes from AS to neighboring AS.
最近では、option d)なるものも提案されているみたいです。
技術詳細は、過去のCisco講演スライドなどを参考にしてください。
http://www.cisco.com/web/SK/expo2009/docs/C3_InterAS_MPLSSolutions_StefanKollar.pdf
◆Ryu BGP検証環境
InterAS MPLS VPN網では、ASBRなるエッジルータの役割が非常に重要となります。
Ryu BGPでのASBR/PE機能を有するエッジルータ適用を想定したOptionBモデルを構築します。実際のBGP/MPLS VPN網は、GNS3によるCiscoエミュレータソフトを活用しました。
(1) Ryu BGPでの実装コード
Ryu公式ドキュメントでの、API仕様やサンプルコードを参考に、検証用のサンプルコードを準備しました。
RyuBGP1側の実装コード(sampleBGP_for_InterAS-MPLS-VPN-in-RyuBGP1.py )
import eventlet import time eventlet.monkey_patch() import logging import sys logging.basicConfig(level=logging.INFO) from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker def dump_remote_best_path_change(event): print 'the best path changed:', event.remote_as, event.prefix,\ event.nexthop, event.is_withdraw if __name__ == "__main__": speaker = BGPSpeaker(as_number=65001, router_id='10.0.0.7', best_path_change_handler=dump_remote_best_path_change, ssh_console=True, label_range=(1000,1999)) speaker.neighbor_add('192.168.100.101', 65010, enable_ipv4=True, enable_vpnv4=True) speaker.neighbor_add('172.16.0.102', 65002, enable_ipv4=True, enable_vpnv4=True) speaker.vrf_add('65010:101', ['65010:101'], ['65010:101']) eventlet.sleep(5) speaker.prefix_add('192.168.103.0/30', next_hop='0.0.0.0', route_dist='65010:101') while True: eventlet.sleep(5)
RyuBGP2側の実装コード(sampleBGP_for_InterAS-MPLS-VPN-in-RyuBGP2.py )
import eventlet import time eventlet.monkey_patch() import logging import sys logging.basicConfig(level=logging.DEBUG) from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker def dump_remote_best_path_change(event): print 'the best path changed:', event.remote_as, event.prefix,\ event.nexthop, event.is_withdraw if __name__ == "__main__": speaker = BGPSpeaker(as_number=65002, router_id='10.0.0.8', best_path_change_handler=dump_remote_best_path_change, ssh_console=True, label_range=(2000,2999)) speaker.neighbor_add('192.168.101.101', 65010, enable_ipv4=True, enable_vpnv4=True) speaker.neighbor_add('172.16.0.101', 65001, enable_ipv4=True, enable_vpnv4=True) speaker.vrf_add('65010:101', ['65010:101'], ['65010:101']) eventlet.sleep(5) speaker.prefix_add('192.168.104.0/30', next_hop='0.0.0.0', route_dist='65010:101') while True: eventlet.sleep(5)
◆InterAS MPLS VPN検証結果
今回の検証観点は、
1. Ryu BGPでの最適パス選定が正しく動作できること
2. ASBR〜RyuBGP間でのリンク切断時には、BGPピア断を検出して、ルーティング迂回経路が構築できること
3. ASBR〜RyuBGP間でのリンク復旧時には、BGPピア開設を検出して、ルーティング経路が再構築できること
1. Ryu BGPでの最適パス選定の動作
既存のBGP/MPLS VPN網とのOptionBによる相互接続により、Ryu BGP上でのBGPテーブルが正しく構築されることを確認します。
(1) RyuBGP1上のBGP経路結果
RyuBGP1でのBGP経路情報を確認してみました。rib上ではASパス長での最適パス選定が実施された様子が確認できました。さらに、ribでの最適パス選定結果が、vrf上のルーティングテーブルに注入されている様子が確認できます。
Ryu BGPでの最適パス計算における選定理由が、showコマンド結果で把握できる特徴があります。
これから、BGP技術知識を深めたい場合の学習教材としての活用には、最適な選択肢だと思います。
なお、Ryu BGP1〜CE3間のコネク区間のスタティック経路を設定sる際には、MPLSラベルとして1000番台をアサインするようにしております。
bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:192.168.103.0/30 [1000] 172.16.0.101 AS Path 65001 ? * [30] 192.168.101.101 65010 65001 ? * 65010:101:192.168.202.0/24 [31] 172.16.0.101 65001 65010 65012 i *> [32] 192.168.101.101 AS Path 65010 65012 i * 65010:101:192.168.201.0/24 [32] 172.16.0.101 65001 65010 65011 i *> [33] 192.168.101.101 AS Path 65010 65011 i * 65010:101:192.168.2.0/30 [29] 172.16.0.101 65001 65010 ? *> [29] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.1/32 [33] 172.16.0.101 65001 65010 65011 ? *> [34] 192.168.101.101 AS Path 65010 65011 ? *> 65010:101:192.168.104.0/30 [2000] 0.0.0.0 Only Path ? * 65010:101:192.168.1.0/30 [28] 172.16.0.101 65001 65010 ? *> [28] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.2/32 [30] 172.16.0.101 65001 65010 65012 ? *> [31] 192.168.101.101 AS Path 65010 65012 ? bgpd> show vrf all Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path VPN: ('65010:101', 'ipv4') *> 192.168.202.0/24 None 192.168.101.101 Only Path 65010 65012 i *> 10.10.10.1/32 None 192.168.101.101 Only Path 65010 65011 ? *> 192.168.104.0/30 None 0.0.0.0 Only Path ? *> 10.10.10.2/32 None 192.168.101.101 Only Path 65010 65012 ? *> 192.168.201.0/24 None 192.168.101.101 Only Path 65010 65011 i *> 192.168.1.0/30 None 192.168.101.101 Only Path 65010 ? *> 192.168.103.0/30 None 172.16.0.101 Only Path 65001 ? *> 192.168.2.0/30 None 192.168.101.101 Only Path 65010 ? bgpd>
(2) RyuBGP2上のBGP経路結果
RyuBGP2でのBGP経路情報を確認してみました。rib上ではASパス長での最適パス選定が実施された様子が確認できました。さらに、ribでの最適パス選定結果が、vrf上のルーティングテーブルに注入されている様子が確認できます。なお、Ryu BGP2〜CE4間のコネク区間のスタティック経路を設定sる際には、MPLSラベルとして、2000番台をアサインするようにしております。
bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:192.168.103.0/30 [1000] 172.16.0.101 AS Path 65001 ? * [30] 192.168.101.101 65010 65001 ? * 65010:101:192.168.202.0/24 [31] 172.16.0.101 65001 65010 65012 i *> [32] 192.168.101.101 AS Path 65010 65012 i * 65010:101:192.168.201.0/24 [32] 172.16.0.101 65001 65010 65011 i *> [33] 192.168.101.101 AS Path 65010 65011 i * 65010:101:192.168.2.0/30 [29] 172.16.0.101 65001 65010 ? *> [29] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.1/32 [33] 172.16.0.101 65001 65010 65011 ? *> [34] 192.168.101.101 AS Path 65010 65011 ? *> 65010:101:192.168.104.0/30 [2000] 0.0.0.0 Only Path ? * 65010:101:192.168.1.0/30 [28] 172.16.0.101 65001 65010 ? *> [28] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.2/32 [30] 172.16.0.101 65001 65010 65012 ? *> [31] 192.168.101.101 AS Path 65010 65012 ? bgpd> show vrf all Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path VPN: ('65010:101', 'ipv4') *> 192.168.202.0/24 None 192.168.101.101 Only Path 65010 65012 i *> 10.10.10.1/32 None 192.168.101.101 Only Path 65010 65011 ? *> 192.168.104.0/30 None 0.0.0.0 Only Path ? *> 10.10.10.2/32 None 192.168.101.101 Only Path 65010 65012 ? *> 192.168.201.0/24 None 192.168.101.101 Only Path 65010 65011 i *> 192.168.1.0/30 None 192.168.101.101 Only Path 65010 ? *> 192.168.103.0/30 None 172.16.0.101 Only Path 65001 ? *> 192.168.2.0/30 None 192.168.101.101 Only Path 65010 ? bgpd>
(3) BGP/MPLS VPN網でのBGP経路結果
BGP/MPLS VPN網でのBGP経路情報を確認してみました。
ASBR3でのルーティング確認
ASBR3>show bgp vpnv4 unicast all BGP table version is 24, local router ID is 10.0.0.3 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *> 192.168.103.0/30 192.168.100.102 0 65001 ? *>i 192.168.104.0/30 10.0.0.6 0 100 0 65002 ? * 192.168.100.102 0 65001 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i ASBR3>show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 33/28 10.10.10.2/32 10.0.0.4 30/28 192.168.1.0/30 10.0.0.1 28/29 192.168.2.0/30 10.0.0.4 29/29 192.168.103.0/30 192.168.100.102 35/1000 192.168.104.0/30 10.0.0.6 36/37 192.168.100.102 36/2000 192.168.201.0 10.0.0.1 32/30 192.168.202.0 10.0.0.4 31/30
ASBR4でのルーティング確認
ASBR4>show bgp vpnv4 unicast all BGP table version is 20, local router ID is 10.0.0.6 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? * 192.168.103.0/30 192.168.101.102 0 65002 65001 ? *>i 10.0.0.3 0 100 0 65001 ? *> 192.168.104.0/30 192.168.101.102 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i ASBR4>show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 34/28 10.10.10.2/32 10.0.0.4 31/28 192.168.1.0/30 10.0.0.1 28/29 192.168.2.0/30 10.0.0.4 29/29 192.168.103.0/30 192.168.101.102 30/1000 10.0.0.3 30/35 192.168.104.0/30 192.168.101.102 37/2000 192.168.201.0 10.0.0.1 33/30 192.168.202.0 10.0.0.4 32/30
RR1でのルーティング確認
RR1#show bgp vpnv4 unicast all BGP table version is 22, local router ID is 10.0.0.7 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *>i 192.168.103.0/30 10.0.0.3 0 100 0 65001 ? *>i 192.168.104.0/30 10.0.0.6 0 100 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i RR1#show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 nolabel/28 10.10.10.2/32 10.0.0.4 nolabel/28 192.168.1.0/30 10.0.0.1 nolabel/29 192.168.2.0/30 10.0.0.4 nolabel/29 192.168.103.0/30 10.0.0.3 nolabel/35 192.168.104.0/30 10.0.0.6 nolabel/37 192.168.201.0 10.0.0.1 nolabel/30 192.168.202.0 10.0.0.4 nolabel/30
2."ASBR3〜RyuBGP1間"のリンク切断時の動作結果
既存のBGP/MPLS VPN網とのOptionBによる相互接続において、対向ASBRからのmp-eBGPピアが切断した場合に、Ryu BGP上でのBGPテーブルが正しく構築されることを確認します。
(1) RyuBGP1上のBGP経路結果
"ASBR3〜RyuBGP1間"でのリンク断の障害を発生させた上で、RyuBGP1でのBGP経路情報を確認してみました。ASBR3からのBGP経路広告が途絶したことにより、rib上では最適パス選定が再実施された様子が確認できました。
bgpd> bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:192.168.103.0/30 [1000] 0.0.0.0 Only Path ? *> 65010:101:192.168.104.0/30 [2000] 172.16.0.102 Only Path 65002 ? *> 65010:101:192.168.1.0/30 [28] 172.16.0.102 Only Path 65002 65010 ? *> 65010:101:192.168.2.0/30 [29] 172.16.0.102 Only Path 65002 65010 ? *> 65010:101:10.10.10.2/32 [31] 172.16.0.102 Only Path 65002 65010 65012 ? *> 65010:101:192.168.202.0/24 [32] 172.16.0.102 Only Path 65002 65010 65012 i *> 65010:101:192.168.201.0/24 [33] 172.16.0.102 Only Path 65002 65010 65011 i *> 65010:101:10.10.10.1/32 [34] 172.16.0.102 Only Path 65002 65010 65011 ?
(2) RyuBGP2上のBGP経路結果
"ASBR3〜RyuBGP1間"でのリンク断の障害を発生させた上で、RyuBGP2でのBGP経路情報を確認してみました。ASBR3からのBGP経路広告が途絶したことにより、rib上では最適パス選定が再実施された様子が確認できました。
bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:192.168.103.0/30 [1000] 172.16.0.101 Only Path 65001 ? * 65010:101:192.168.202.0/24 [31] 172.16.0.101 65001 65010 65012 i *> [32] 192.168.101.101 AS Path 65010 65012 i * 65010:101:192.168.201.0/24 [32] 172.16.0.101 65001 65010 65011 i *> [33] 192.168.101.101 AS Path 65010 65011 i * 65010:101:192.168.2.0/30 [29] 172.16.0.101 65001 65010 ? *> [29] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.1/32 [33] 172.16.0.101 65001 65010 65011 ? *> [34] 192.168.101.101 AS Path 65010 65011 ? *> 65010:101:192.168.104.0/30 [2000] 0.0.0.0 Only Path ? * 65010:101:192.168.1.0/30 [28] 172.16.0.101 65001 65010 ? *> [28] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.2/32 [30] 172.16.0.101 65001 65010 65012 ? *> [31] 192.168.101.101 AS Path 65010 65012 ?
(3) BGP/MPLS VPN網でのルーティング動作確認
BGP/MPLS VPN網でのBGP経路情報を確認してみました。
ASBR3でのルーティング確認
ASBR3>show bgp vpnv4 unicast all BGP table version is 30, local router ID is 10.0.0.3 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *>i 192.168.103.0/30 10.0.0.6 0 100 0 65002 65001 ? *>i 192.168.104.0/30 10.0.0.6 0 100 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i ASBR3>show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 33/28 10.10.10.2/32 10.0.0.4 30/28 192.168.1.0/30 10.0.0.1 28/29 192.168.2.0/30 10.0.0.4 29/29 192.168.103.0/30 10.0.0.6 35/30 192.168.104.0/30 10.0.0.6 36/37 192.168.201.0 10.0.0.1 32/30 192.168.202.0 10.0.0.4 31/30
ASBR4でのルーティング確認
ASBR4>show bgp vpnv4 unicast all BGP table version is 25, local router ID is 10.0.0.6 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *> 192.168.103.0/30 192.168.101.102 0 65002 65001 ? *> 192.168.104.0/30 192.168.101.102 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i ASBR4>show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 34/28 10.10.10.2/32 10.0.0.4 31/28 192.168.1.0/30 10.0.0.1 28/29 192.168.2.0/30 10.0.0.4 29/29 192.168.103.0/30 192.168.101.102 30/1000 192.168.104.0/30 192.168.101.102 37/2000 192.168.201.0 10.0.0.1 33/30 192.168.202.0 10.0.0.4 32/30
RR1でのルーティング確認
RR1#show bgp vpnv4 unicast all BGP table version is 28, local router ID is 10.0.0.7 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *>i 192.168.103.0/30 10.0.0.6 0 100 0 65002 65001 ? *>i 192.168.104.0/30 10.0.0.6 0 100 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i RR1#show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 nolabel/28 10.10.10.2/32 10.0.0.4 nolabel/28 192.168.1.0/30 10.0.0.1 nolabel/29 192.168.2.0/30 10.0.0.4 nolabel/29 192.168.103.0/30 10.0.0.6 nolabel/30 192.168.104.0/30 10.0.0.6 nolabel/37 192.168.201.0 10.0.0.1 nolabel/30 192.168.202.0 10.0.0.4 nolabel/30
3."ASBR3〜RyuBGP1間"のリンク復旧時の動作結果
既存のBGP/MPLS VPN網とのOptionBによる相互接続において、対向ASBRからのmp-eBGPピア断から回復した場合に、Ryu BGP上でのBGPテーブルが正しく構築されることを確認します。
(1) RyuBGP1上のBGP経路結果
"ASBR3〜RyuBGP1間"でのリンク断を回復させた上で、RyuBGP1でのBGP経路情報を確認してみました。ASBR3からのBGP経路広告が再開したことにより、rib上では最適パス選定が再実施された様子が確認できました。
bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:192.168.103.0/30 [1000] 0.0.0.0 Only Path ? *> 65010:101:192.168.104.0/30 [2000] 172.16.0.102 AS Path 65002 ? * [36] 192.168.100.101 65010 65002 ? *> 65010:101:192.168.1.0/30 [28] 192.168.100.101 AS Path 65010 ? * [28] 172.16.0.102 65002 65010 ? *> 65010:101:192.168.2.0/30 [29] 192.168.100.101 AS Path 65010 ? * [29] 172.16.0.102 65002 65010 ? *> 65010:101:10.10.10.2/32 [30] 192.168.100.101 AS Path 65010 65012 ? * [31] 172.16.0.102 65002 65010 65012 ? *> 65010:101:192.168.202.0/24 [31] 192.168.100.101 AS Path 65010 65012 i * [32] 172.16.0.102 65002 65010 65012 i *> 65010:101:192.168.201.0/24 [32] 192.168.100.101 AS Path 65010 65011 i * [33] 172.16.0.102 65002 65010 65011 i *> 65010:101:10.10.10.1/32 [33] 192.168.100.101 AS Path 65010 65011 ? * [34] 172.16.0.102 65002 65010 65011 ?
(2) RyuBGP2上のBGP経路結果
"ASBR3〜RyuBGP1間"でのリンク断を回復させた上で、RyuBGP2でのBGP経路情報を確認してみました。ASBR3からのBGP経路広告が再開したことにより、rib上では最適パス選定が再実施された様子が確認できました。
bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:192.168.103.0/30 [1000] 172.16.0.101 AS Path 65001 ? * [30] 192.168.101.101 65010 65001 ? * 65010:101:192.168.202.0/24 [31] 172.16.0.101 65001 65010 65012 i *> [32] 192.168.101.101 AS Path 65010 65012 i * 65010:101:192.168.201.0/24 [32] 172.16.0.101 65001 65010 65011 i *> [33] 192.168.101.101 AS Path 65010 65011 i * 65010:101:192.168.2.0/30 [29] 172.16.0.101 65001 65010 ? *> [29] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.1/32 [33] 172.16.0.101 65001 65010 65011 ? *> [34] 192.168.101.101 AS Path 65010 65011 ? *> 65010:101:192.168.104.0/30 [2000] 0.0.0.0 Only Path ? * 65010:101:192.168.1.0/30 [28] 172.16.0.101 65001 65010 ? *> [28] 192.168.101.101 AS Path 65010 ? * 65010:101:10.10.10.2/32 [30] 172.16.0.101 65001 65010 65012 ? *> [31] 192.168.101.101 AS Path 65010 65012 ?
(3) BGP/MPLS VPN網でのルーティング動作確認
BGP/MPLS VPN網でのBGP経路情報を確認してみました。
ASBR3でのルーティング確認
ASBR3>show bgp vpnv4 unicast all BGP table version is 47, local router ID is 10.0.0.3 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *> 192.168.103.0/30 192.168.100.102 0 65001 ? *>i 192.168.104.0/30 10.0.0.6 0 100 0 65002 ? * 192.168.100.102 0 65001 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i ASBR3>show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 33/28 10.10.10.2/32 10.0.0.4 30/28 192.168.1.0/30 10.0.0.1 28/29 192.168.2.0/30 10.0.0.4 29/29 192.168.103.0/30 192.168.100.102 35/1000 192.168.104.0/30 192.168.100.102 36/2000 10.0.0.6 36/37 192.168.201.0 10.0.0.1 32/30 192.168.202.0 10.0.0.4 31/30
ASBR4でのルーティング確認
ASBR4>show bgp vpnv4 unicast all BGP table version is 45, local router ID is 10.0.0.6 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *>i 192.168.103.0/30 10.0.0.3 0 100 0 65001 ? * 192.168.101.102 0 65002 65001 ? *> 192.168.104.0/30 192.168.101.102 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i ASBR4>show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 34/28 10.10.10.2/32 10.0.0.4 31/28 192.168.1.0/30 10.0.0.1 28/29 192.168.2.0/30 10.0.0.4 29/29 192.168.103.0/30 10.0.0.3 30/35 192.168.101.102 30/1000 192.168.104.0/30 192.168.101.102 37/2000 192.168.201.0 10.0.0.1 33/30 192.168.202.0 10.0.0.4 32/30
RR1でのルーティング確認
RR1#show bgp vpnv4 unicast all BGP table version is 52, local router ID is 10.0.0.7 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65010:101 *>i 10.10.10.1/32 10.0.0.1 0 100 0 65011 ? *>i 10.10.10.2/32 10.0.0.4 0 100 0 65012 ? *>i 192.168.1.0/30 10.0.0.1 0 100 0 ? *>i 192.168.2.0/30 10.0.0.4 0 100 0 ? *>i 192.168.103.0/30 10.0.0.3 0 100 0 65001 ? *>i 192.168.104.0/30 10.0.0.6 0 100 0 65002 ? *>i 192.168.201.0 10.0.0.1 0 100 0 65011 i *>i 192.168.202.0 10.0.0.4 0 100 0 65012 i RR1#show bgp vpnv4 unicast all labels Network Next Hop In label/Out label Route Distinguisher: 65010:101 10.10.10.1/32 10.0.0.1 nolabel/28 10.10.10.2/32 10.0.0.4 nolabel/28 192.168.1.0/30 10.0.0.1 nolabel/29 192.168.2.0/30 10.0.0.4 nolabel/29 192.168.103.0/30 10.0.0.3 nolabel/35 192.168.104.0/30 10.0.0.6 nolabel/37 192.168.201.0 10.0.0.1 nolabel/30 192.168.202.0 10.0.0.4 nolabel/30
Ryu SDN FrameworkのBGP機能を試してみた(1) 〜BGP/MPLS網でのエッジルータ適用編〜
最新のRyu SDN Frameworkでは、BGPルーティング機能(以下、Ryu BGP)が活用できるようになったらしいです。
Ryu公式ドキュメントにも、API仕様やサンプルコードが公開されておりますね。
BGP speaker library API Reference — Ryu 3.12 documentation
BGP speaker library — Ryu 3.12 documentation
ここで注目したい点は、通信キャリアで活用されているBGP/MPLS VPN網の通信プロトコルに対応しているところなのです。
まだまだ活用事例が少ないので、最初から手探り状態で、どの程度、基本動作が可能なのかを試してみました。
◆そもそも、BGP/MPLS VPNとは ...
BGP/MPLS VPNによるC-Plane/D-Plane分離に関わる通信プロトコル基本動作原理の解説については、ブログ記事の領域を超越してしまうため、割愛させて頂きます。この技術分野に興味を持たれる方は、こちらの文献が一読されることを、お勧め致します。
- 作者: 友近剛史,池尻雄一
- 出版社/メーカー: 翔泳社
- 発売日: 2014/05/23
- メディア: 大型本
- この商品を含むブログを見る
昔のインターネット普及以前では、企業ユーザの各拠点間を専用線などの占有タイプのネットワークサービスを活用して相互接続を図っておりました。その後のインターネット技術の台頭あたりから、拠点間の相互接続を通信キャリアが運営する閉域ネットワークに重畳することにより、通信コストの低減を図りつつ、安定的なネットワーク品質のVPN環境を利用する時代に移行してきました。
ここでの、閉域ネットワークのスケール自動化を目的とした管理制御手法として、BGP/MPLS VPN技術が活用されてきました。
もともとは、BGP/MPLS VPNは、通信キャリア分野で活用していた技術でしたが、ルータ機器で動作する通信プロトコルは、ベンダプロプライエタリーな技術領域であり、技術エンジニア自らが、改変できるものではありませんでした。(一部、Quagga等のオープンソース製品はありましたが、)
最近のSDNによるC-Plane/D-Plane分離技術の台頭により、オープンソースベースのBGP/MPLSを活用したDC/WAN領域でのシームレスなマルチテナント制御が注目されております。(最近では、OpenContrailが有名でしょうか。)
ところで、BGP/MPLS VPNを活用したMPLS-VPN網には、どのような特徴があるのでしょうか?
ざっくり言えば、物理トポロジを構成する物理リソースをBGP/MPLS VPNを活用してオートスケールなアクティベーションを可能とする技術となります。
拠点間での相互接続およびテナント分離に関わる基本動作は、概ねエッジルータ(PE)で行うことになります。
さらに、拠点間でのL3経路情報の伝搬および拠点間の通信経路選定に関わる通信制御系の動作と拠点間のデータ転送系の動作は、明示的に分離されている特徴があります。昨今のSDNによるC-Plane/D-Plane分離技術に通じるものです。
◆Ryu BGP検証環境
BGP/MPLS VPN網ではエッジルータの役割が非常に重要ということになりますので、Ryu BGP機能のエッジルータ適用を想定した検証モデルを構築します。実際のBGP/MPLS VPN網は、GNS3によるCiscoエミュレータソフトを活用しました。
1. 制御系ネットワーク
本来のBGP/MPLS VPN網でのエッジルータでは、エッジルータ間の相互接続としてOSPF, LDPなどのトンネル・ルーティング制御が必要不可欠となるわけですが、今回は、この技術領域の検証は行いません。ただし、BGPプロトコルでは、NextHop指定として、Ryu BGP〜各PE間での到達性が必要となるため、Ryu BGPサーバも各PE間で構築されたOSPFルーティング面に参加される必要がありました。今回は、Ryu BGPサーバ自体でOSPFプロトコルを扱うことはせず、Ryu BGP〜RR間をパッシブインターフェース区間として扱いました。
2. 転送系ネットワーク
本来、BGPプロトコルで学習した拠点間経路情報をデータプレーンに注入する必要があります。
ただし、これを実現するために要素技術として、OpenFlow連携等を想定しなければなりません。
そもそも、BGP/MPLS VPN網でのエッジルータ間でデータプレーンを構築するためには、別途、LDPプロトコルの実装も必要になるため、今回は対象外としました。
近い将来、エッジルータ間をMPLSベースのトンネルでは無く、GREベースのトンネル技術を活用した転送系ネットワーク構築にチャレンジしたいと考えています。
3. Ryu BGPでの実装コード
Ryu公式ドキュメントでの、API仕様やサンプルコードを参考に、検証用のサンプルコード(sampleBGP_for_MPLS-VPN.py)を準備しました。
import eventlet import time eventlet.monkey_patch() import logging import sys logging.basicConfig(level=logging.INFO) from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker def dump_remote_best_path_change(event): print 'the best path changed:', event.remote_as, event.prefix,\ event.nexthop, event.is_withdraw if __name__ == "__main__": speaker = BGPSpeaker(as_number=65010, router_id='10.0.0.8', best_path_change_handler=dump_remote_best_path_change, ssh_console=True) speaker.neighbor_add('192.168.100.100', 65010, enable_ipv4=True, enable_vpnv4=True) speaker.vrf_add('65010:101', ['65010:101'], ['65010:101']) eventlet.sleep(5) speaker.prefix_add('192.168.104.0/30', next_hop='0.0.0.0', route_dist='65010:101') while True: eventlet.sleep(5)
◆Ryu BGP動作結果
1. 事前のBGPテーブル確認
CE1でのBGPテーブルには、CE1, CE2, CE3に関わるプレフィックス情報が出力されております。
Ryu BGPの起動前であるため、CE4に関わるプレフィックス情報は出力されておりません。
CE1#sh bgp ipv4 unicast BGP table version is 10, local router ID is 10.10.10.1 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 10.10.10.1/32 0.0.0.0 0 32768 ? *> 10.10.10.2/32 192.168.101.2 0 65010 65012 ? *> 10.10.10.3/32 192.168.101.2 0 65010 65013 ? * 192.168.101.0/30 192.168.101.2 0 0 65010 ? *> 0.0.0.0 0 32768 ? *> 192.168.102.0/30 192.168.101.2 0 65010 ? *> 192.168.103.0/30 192.168.101.2 0 65010 ? *> 192.168.201.0 0.0.0.0 0 32768 i *> 192.168.202.0 192.168.101.2 0 65010 65012 i *> 192.168.203.0 192.168.101.2 0 65010 65013 i
2. Ryu BGPサンプルコード実行開始
サンプルコード(sampleBGP_for_MPLS-VPN.py)では、以下の処理を実行します。
(1) RR〜Ryu BGP間でBGPピアを開設
(2) VRF登録
(3) Ryu BGP〜CE4間のコネク区間(192.168.104.0/30)の経路情報登録
その実行結果です。
$ sudo python ./sampleBGP_for_MPLS-VPN.py [sudo] password for tsubo: INFO:bgpspeaker.api.base:API method core.start called with args: {'router_id': '10.0.0.8', 'waiter': <ryu.lib.hub.Event object at 0x7fe52d3ab450>, 'bgp_server_port': 179, 'local_as': 65010, 'refresh_max_eor_time': 0, 'refresh_stalepath_time': 0} INFO:bgpspeaker.api.base:API method neighbor.create called with args: {'remote_as': 65010, 'cap_mbgp_vpnv6': False, 'cap_mbgp_vpnv4': True, 'cap_mbgp_ipv6': False, 'cap_mbgp_ipv4': True, 'is_route_server_client': False, 'peer_next_hop': None, 'password': None, 'ip_address': '192.168.100.100'} INFO:bgpspeaker.api.base:API method vrf.create called with args: {'import_rts': ['65010:101'], 'route_family': 'ipv4', 'site_of_origins': None, 'route_dist': '65010:101', 'export_rts': ['65010:101']} INFO:bgpspeaker.cli:starting ssh server at localhost:4990 INFO:bgpspeaker.peer:Connection to peer: 192.168.100.100 established INFO:bgpspeaker.api.base:API method prefix.add_local called with args: {'prefix': '192.168.104.0/30', 'next_hop': '0.0.0.0', 'route_dist': '65010:101', 'route_family': 'ipv4'} .. (snip)
3. Ryu BGPでのCLIによりBGPテーブル等の状態確認
RR〜Ryu BGP間でのBGPピア開設/VRF登録に伴い、BGP/MPLS VPN網からのVRFプレフィックス情報を取得できている様子が確認できます。さらに、Ryu BGP〜CE4間のコネク区間(192.168.104.0/30)の経路情報も確認できます。
$ ssh localhost -p 4990 Hello, this is Ryu BGP speaker (version 3.12). bgpd> show vrf 65010:101 ipv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 192.168.202.0/24 None 10.0.0.4 Only Path 0 100 65012 i *> 10.10.10.1/32 None 10.0.0.1 Only Path 0 100 65011 ? *> 192.168.102.0/30 None 10.0.0.4 Only Path 0 100 ? *> 192.168.104.0/30 None 0.0.0.0 Only Path ? *> 10.10.10.3/32 None 10.0.0.3 Only Path 0 100 65013 ? *> 192.168.203.0/24 None 10.0.0.3 Only Path 0 100 65013 i *> 10.10.10.2/32 None 10.0.0.4 Only Path 0 100 65012 ? *> 192.168.201.0/24 None 10.0.0.1 Only Path 0 100 65011 i *> 192.168.103.0/30 None 10.0.0.3 Only Path 0 100 ? *> 192.168.101.0/30 None 10.0.0.1 Only Path 0 100 ? bgpd> bgpd> show rib vpnv4 Status codes: * valid, > best Origin codes: i - IGP, e - EGP, ? - incomplete Network Labels Next Hop Reason Metric LocPrf Path *> 65010:101:10.10.10.3/32 [29] 10.0.0.3 Only Path 0 100 65013 ? *> 65010:101:192.168.202.0/24 [30] 10.0.0.4 Only Path 0 100 65012 i *> 65010:101:10.10.10.1/32 [29] 10.0.0.1 Only Path 0 100 65011 ? *> 65010:101:192.168.101.0/30 [30] 10.0.0.1 Only Path 0 100 ? *> 65010:101:192.168.201.0/24 [31] 10.0.0.1 Only Path 0 100 65011 i *> 65010:101:192.168.103.0/30 [30] 10.0.0.3 Only Path 0 100 ? *> 65010:101:192.168.102.0/30 [29] 10.0.0.4 Only Path 0 100 ? *> 65010:101:10.10.10.2/32 [28] 10.0.0.4 Only Path 0 100 65012 ? *> 65010:101:192.168.203.0/24 [31] 10.0.0.3 Only Path 0 100 65013 i *> 65010:101:192.168.104.0/30 [100] 0.0.0.0 Only Path ? bgpd>
4. CE1でのCLIによりBGPテーブル等の状態確認
RR〜Ryu BGP間でのBGPピア開設/VRF登録に伴い、Ryu BGPからのVRFプレフィックス情報を取得できている様子が確認できます。さらに、Ryu BGP〜CE4間のコネク区間(192.168.104.0/30)の経路情報も確認できます。
CE1#sh bgp ipv4 unicast BGP table version is 13, local router ID is 10.10.10.1 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, x best-external, a additional-path, c RIB-compressed, Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 10.10.10.1/32 0.0.0.0 0 32768 ? *> 10.10.10.2/32 192.168.101.2 0 65010 65012 ? *> 10.10.10.3/32 192.168.101.2 0 65010 65013 ? * 192.168.101.0/30 192.168.101.2 0 0 65010 ? *> 0.0.0.0 0 32768 ? *> 192.168.102.0/30 192.168.101.2 0 65010 ? *> 192.168.103.0/30 192.168.101.2 0 65010 ? *> 192.168.104.0/30 192.168.101.2 0 65010 ? *> 192.168.201.0 0.0.0.0 0 32768 i *> 192.168.202.0 192.168.101.2 0 65010 65012 i *> 192.168.203.0 192.168.101.2 0 65010 65013 i
◆おまけ(BGP Update Messageパケットの各種パラメータ値をのぞいてみる)
Ryu BGPでは、BGPピアでの送受信単位にBGP Update Messageパケットの各種パラメータ値を確認することができます。各種アドレスファミリー毎に確認するも可能です。
複数のRyu BGPサーバ間でBGPピアを開設した上で、BGP Update Messageパケットの各種パラメータ値をのぞいてみると、BGP学習環境を簡単に構築できるメリットがあると思います。
1. Ryu BGP側でのmpBGP Update Messageの確認
さきほどの「◆Ryu BGP動作結果」後に、Ryu BGPのCLIから、RRに送信したBGP Update Message
bgpd> show neighbor sent-routes 192.168.100.100 all {'65010:101:192.168.104.0/30': {'as_path': [], 'is_withdraw': False, 'local_pref': None, 'metric': None, 'nexthop': '0.0.0.0', 'nlri': {'formatted_nlri': '65010:101:192.168.104.0/30', 'labels': [100], 'prefix': '192.168.104.0/30', 'rd': '65010:101'}, 'origin': 2, 'route_family': {'afi': 1, 'safi': 128}, 'rt_list': ['65010:101'], 'soo_list': [], 'source_version_num': 0}}
2. 既存BGP/MPLS側でのmpBGP Update Messageの確認
PE1側で受信したBGP Update Messageパケットの各種パラメータ値を確認しました。
Ryu BGPのCLIで確認したNextHop値と、PE1側で受信したNextHop値が異なる点については、今度とも留意が必要かもしれません。
No. Time Source Destination Protocol Length Info 1247 1030.151634000 10.0.0.7 10.0.0.1 BGP 152 UPDATE Message Frame 1247: 152 bytes on wire (1216 bits), 152 bytes captured (1216 bits) on interface 0 Ethernet II, Src: ca:06:09:87:00:1c (ca:06:09:87:00:1c), Dst: ca:04:09:78:00:1d (ca:04:09:78:00:1d) Internet Protocol Version 4, Src: 10.0.0.7 (10.0.0.7), Dst: 10.0.0.1 (10.0.0.1) Transmission Control Protocol, Src Port: bgp (179), Dst Port: 49516 (49516), Seq: 343, Ack: 362, Len: 98 Border Gateway Protocol - UPDATE Message Marker: ffffffffffffffffffffffffffffffff Length: 98 Type: UPDATE Message (2) Unfeasible routes length: 0 bytes Total path attribute length: 75 bytes Path attributes ORIGIN: INCOMPLETE (4 bytes) Flags: 0x40 (Well-known, Transitive, Complete) 0... .... = Optional: Well-known .1.. .... = Transitive: Transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: ORIGIN (1) Length: 1 byte Origin: INCOMPLETE (2) AS_PATH: empty (3 bytes) Flags: 0x40 (Well-known, Transitive, Complete) 0... .... = Optional: Well-known .1.. .... = Transitive: Transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: AS_PATH (2) Length: 0 bytes AS path: empty LOCAL_PREF: 100 (7 bytes) Flags: 0x40 (Well-known, Transitive, Complete) 0... .... = Optional: Well-known .1.. .... = Transitive: Transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: LOCAL_PREF (5) Length: 4 bytes Local preference: 100 EXTENDED_COMMUNITIES: (11 bytes) Flags: 0xc0 (Optional, Transitive, Complete) 1... .... = Optional: Optional .1.. .... = Transitive: Transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: EXTENDED_COMMUNITIES (16) Length: 8 bytes Carried Extended communities two-octet AS specific Route Target: 65010:101 CLUSTER_LIST: 10.0.0.7 (7 bytes) Flags: 0x80 (Optional, Non-transitive, Complete) 1... .... = Optional: Optional .0.. .... = Transitive: Non-transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: CLUSTER_LIST (10) Length: 4 bytes Cluster list: 10.0.0.7 Cluster List: 0a000007 ORIGINATOR_ID: 10.0.0.8 (7 bytes) Flags: 0x80 (Optional, Non-transitive, Complete) 1... .... = Optional: Optional .0.. .... = Transitive: Non-transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: ORIGINATOR_ID (9) Length: 4 bytes Originator identifier: 10.0.0.8 (10.0.0.8) MP_REACH_NLRI (36 bytes) Flags: 0x80 (Optional, Non-transitive, Complete) 1... .... = Optional: Optional .0.. .... = Transitive: Non-transitive ..0. .... = Partial: Complete ...0 .... = Length: Regular length Type code: MP_REACH_NLRI (14) Length: 33 bytes Address family: IPv4 (1) Subsequent address family identifier: Labeled VPN Unicast (128) Next hop network address (12 bytes) Next hop: Empty Label Stack RD=0:0 IPv4=192.168.100.102 (12) Subnetwork points of attachment: 0 Network layer reachability information (16 bytes) Label Stack=100 (bottom) RD=65010:101, IPv4=192.168.104.0/30 MP Reach NLRI Prefix length: 118 MP Reach NLRI Label Stack: 100 (bottom) MP Reach NLRI Route Distinguisher: 65010:101 MP Reach NLRI IPv4 prefix: 192.168.104.0 (192.168.104.0)
◆おわりに
今回は、最新のRyu SDN Frameworkにおける、BGPルーティング機能の動作確認として、BGP/MPLS VPN網で同一AS内でのエッジルータ適用を想定した検証モデルで試してみました。BGPについては、アドレスファミリー(IPv4, VPNv4)において、問題なく動作しておりました。ただし、エッジルータ適用を想定した場合、コントロールプレーンでの他のプロトコル連携の実現形態を想定するとなると、相当なるハードルが待ち構えている点が懸念されます。将来のデータプレーン連携を想定した場合、BGP/MPLS VPN網とのInter-AS接続形態のほうが、基本動作がシンプルになると想定されるます。現状のRyu BGPを、BGP/MPLS VPN網でのエッジルータとして実網に適用するユースケースは現実解として適切ではないと思われます。
OpenStackメッセージング機構を探ってみる(4) 〜Call(情報照会)編〜
前回につづいて、OpenStackメッセージング機構を探ってみる。
◆前回までのおさらい
OpenStackメッセージング機構を体感するために、以下の例題を解決する実現モデルを考えてみる。
1. 例題
実験環境は、サーバ装置1台と、クライアント装置2台で構成されている。
ある特定のクライアント装置が、サーバ装置のマシン時刻を知りたい場合、どうような解決方法が存在するのだろうか?
2. 実現モデル
ここでは、OpenStackメッセージング機構の理解を目的とするので、実現モデルの優位性とかは問題視しません。
案1)同報通知モデル「定期的に、サーバ装置が、全クライアント装置に、同報的に通知する」
案2)特定通知モデル「定期的に、サーバ装置が、特定のクライアント装置に、通知する。」
案3)情報照会モデル「特定のクライアント装置が、サーバ装置にマシン時刻を問い合わせる」
前回までに、案1と案2での実現レベルを確認した。
OpenStackメッセージング機構を探ってみる(2) 〜fanout(同報通知)編〜 - SDN開発エンジニアを目指した活動ブログ
OpenStackメッセージング機構を探ってみる(3) 〜Cast(特定通知)編〜 - SDN開発エンジニアを目指した活動ブログ
◆案3「情報照会モデル」の事前調査
前回と同様に、OpenStackメッセージング概要を理解するために、OpenStack Developerページを確認してみた。
AMQP and Nova — nova 2014.2.dev38.ga8b2eaa documentation
以下は、同サイトからの引用。
RPC Calls
The diagram below shows the message flow during an rpc.call operation:1. a Topic Publisher is instantiated to send the message request to the queuing system; immediately before the publishing operation, a Direct Consumer is instantiated to wait for the response message.
2. once the message is dispatched by the exchange, it is fetched by the Topic Consumer dictated by the routing key (such as ‘topic.host’) and passed to the Worker in charge of the task.
3. once the task is completed, a Direct Publisher is allocated to send the response message to the queuing system.
4. once the message is dispatched by the exchange, it is fetched by the Direct Consumer dictated by the routing key (such as ‘msg_id’) and passed to the Invoker.
RPC Callは、前回までのFanout, castと違って、ちょっと複雑な内部構造になっている。
しかしながら、OpenStack側での共通ライブラリ(という表現が適切か否かは不明だが...)では、まったく内部構造を意識することなく、RPCプログラミングが実現できてしまう点が、Awesome!!
OpenStackメッセージング機構でのRPC Callの理解が深まったところで、さっそく、サンプルコードで試してみる。
◆案3「情報照会モデル」を試してみた
このモデルでは、callというメソッドが用意されている。
サーバ側サンプルコード"receive_call.py"
サーバ側の設定ファイル"server.conf"は、前回と同様のものを使用する。
import os import datetime import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['server.conf']) rpc.set_defaults(control_exchange='sdn') topic = "sample3" my_host = os.uname()[1] class Server(object): def __init__(self): self.dispatcher = dispatcher.RpcDispatcher([self]) self.conn = rpc.create_connection(new=True) self.conn.create_consumer(topic, self.dispatcher) self.conn.consume_in_thread() def func_call(self, context, **kwargs): LOG.debug('received call_request [ hostname=%s, getdate=%s ]' %(kwargs["hostname"],kwargs["getdate"])) return my_host, datetime.datetime.today() if __name__ == '__main__': Server() while True: time.sleep(1)
クライアント側サンプルコード"send_call.py"
クライアント側の設定ファイル"client.conf"は、前回と同様のものを使用する。
import os import datetime import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['client.conf']) rpc.set_defaults(control_exchange='sdn') topic = "sample3" my_host = os.uname()[1] class Client(proxy.RpcProxy): OPTS = [] BASE_RPC_API_VERSION = '1.0' def __init__(self): self.context = context.get_admin_context() super(Client, self).__init__( topic=topic, default_version=self.BASE_RPC_API_VERSION) eventlet.spawn(self._periodic_call) def _periodic_call(self): while True: time.sleep(1) (hostname, getdate) = self.func_call() LOG.debug('received call_reply [ hostname=%s, nowtime=%s ]' %(hostname, getdate)) def func_call(self): method = "func_call" self.getdate = 'now' return self.call( self.context, self.make_msg( method, hostname=my_host, getdate=self.getdate ), topic=self.topic) if __name__ == '__main__': Client() while True: time.sleep(1)
実行結果
サンプルプログラムの実行結果は、こんな感じ。(上段がサーバ装置、下段がクライアント装置)
クライアント装置からサーバ装置のマシン時刻の問い合わせを行うたびに、情報照会されている様子が確認できた。
◆おまけ編
これまで、OpenStackメッセージング実験環境は、Apache Qpidで確認してきた。
折角なので、rabbit-mqでも動作を試してみたい。
qpidの停止
# service qpidd stop
rabbit-mqインストール
# apt-get install rabbitmq-server
クライアント側の設定ファイル"server.conf"
[DEFAULT] rabbit_host= 192.168.100.101 rpc_backend = openstack.common.rpc.impl_kombu #qpid_hostname = 192.168.100.101 #rpc_backend = openstack.common.rpc.impl_qpid
クライアント側の設定ファイル"client.conf"
[DEFAULT] rabbit_host= 192.168.100.101 rpc_backend = openstack.common.rpc.impl_kombu #qpid_hostname = 192.168.100.101 #rpc_backend = openstack.common.rpc.impl_qpid
実行結果
サンプルプログラムの実行結果は、こんな感じ。(上段がサーバ装置、下段がクライアント装置)
設定ファイルの記述修正のみで、"Apache Qpid"から"rabbit-mq"への切り替えが簡単に実現できてしまう点が確認できた。
RabbitMQ Web管理インターフェイス
RabbitMQには、Web管理インターフェイスなるものが存在するらしい。そこで、実際に使ってみた。
第14章 ロギングと監視 - OpenStack 運用ガイド
$ sudo /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management $ sudo service rabbitmq-server restart
(1) Overview
(2) Exchanges
(3) Queues
◆おわりに
実際に、サンプルプログラムを動かしながらOpenStackメッセージング機構の動作を確認してみて、だいぶ理解が深まった。
OpenStackメッセージング機構を活用すれば、SDN用途に限らずに、一般の業務ツールなどのさまざまな局面での適用が期待できる点を、実感できた。
OpenStackメッセージング機構を探ってみる(3) 〜Cast(特定通知)編〜
前回につづいて、OpenStackメッセージング機構を探ってみる。
◆前回のおさらい
OpenStackメッセージング機構を体感するために、以下の例題を解決する実現モデルを考えてみる。
1. 例題
実験環境は、サーバ装置1台と、クライアント装置2台で構成されている。
ある特定のクライアント装置が、サーバ装置のマシン時刻を知りたい場合、どうような解決方法が存在するのだろうか?2. 実現モデル
ここでは、OpenStackメッセージング機構の理解を目的とするので、実現モデルの優位性とかは問題視しません。
案1)同報通知モデル「定期的に、サーバ装置が、全クライアント装置に、同報的に通知する」
案2)特定通知モデル「定期的に、サーバ装置が、特定のクライアント装置に、通知する。」
案3)情報照会モデル「特定のクライアント装置が、サーバ装置にマシン時刻を問い合わせる」
前回は、案1での実現レベルを確認した。
◆案2「特定通知モデル」の事前調査
まず、OpenStackメッセージング概要を理解するために、OpenStack Developerページを確認してみた。
AMQP and Nova — nova 2014.2.dev38.ga8b2eaa documentation
以下は、同サイトからの引用。
RPC Casts
The diagram below the message flow during an rpc.cast operation:
1. A Topic Publisher is instantiated to send the message request to the queuing system.
2. Once the message is dispatched by the exchange, it is fetched by the Topic Consumer dictated by the routing key (such as ‘topic’) and passed to the Worker in charge of the task.
Rabbit mqを前提にした記述になっているが、Apcahe Qpidでも動作原理は同じはず。
OpenStackメッセージング機構でのRPC Castsの理解が深まったところで、さっそく、サンプルコードで試してみる。
あと、routing keyとして、topicキーワードへのホスト名の指定有無によって、動作の違いも気なるため、両方とも試してみる。
◆案2-1:routing keyにホスト名を指定しない場合
このモデルは、PublisherからConsumer宛に特定通知する仕組み。castというメソッドが用意されている。
サーバ側サンプルコード"send_cast1.py"
サーバ側の設定ファイル"server.conf"は、前回と同様のものを使用する。
import os import datetime import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['server.conf']) rpc.set_defaults(control_exchange='sdn') topic = "sample2" my_host = os.uname()[1] class Server(proxy.RpcProxy): OPTS = [] BASE_RPC_API_VERSION = '1.0' def __init__(self): self.context = context.get_admin_context() super(Server, self).__init__( topic=topic, default_version=self.BASE_RPC_API_VERSION) eventlet.spawn(self._periodic_cast) def _periodic_cast(self): while True: time.sleep(1) self.func_cast() def func_cast(self): self.nowtime = datetime.datetime.today() LOG.debug('send cast [ hostname=%s, nowtime=%s ]' %(my_host, self.nowtime)) method = "func_cast" self.cast( self.context, self.make_msg( method, hostname=my_host, nowtime=self.nowtime ), topic=self.topic) if __name__ == '__main__': Server() while True: time.sleep(1)
クライアント側サンプルコード"receive_cast1.py"
クライアント側の設定ファイル"client.conf"は、前回と同様のものを使用する。
import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['client.conf']) rpc.set_defaults(control_exchange='sdn') topic = "sample2" class Client(object): def __init__(self): self.dispatcher = dispatcher.RpcDispatcher([self]) self.conn = rpc.create_connection(new=True) self.conn.create_consumer(topic, self.dispatcher) self.conn.consume_in_thread() def func_cast(self, context, **kwargs): LOG.debug('received cast [ hostname=%s, nowtime=%s ]' %(kwargs["hostname"],kwargs["nowtime"])) if __name__ == '__main__': Client() while True: time.sleep(1)
実行結果
サンプルプログラムの実行結果は、こんな感じ。(上段がサーバ装置、中段、下段がクライアント装置)
約1秒ごとに、上段のサーバ装置のマシン時刻が各クライアント装置に特定通知されている様子が確認できた。
ここでのポイントは、2台のクライアント装置が、同一Topicでメッセージング処理を実施しているため、特定通知のスコープとして、クライアント装置が2台とも含まれている。これにより、サーバ装置が転送したマシン時刻を、クライアント装置の交互に通知される様子が確認できた。
cast動作概要
サンプルプログラム実行結果に関わるcast動作部分を図式化してみた。
◆案2-2:routing keyにホスト名を指定する場合
ここでは、通知先クライアント装置を"Client-2"とした。
サーバ側サンプルコード"send_cast2.py"
import os import datetime import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['server.conf']) rpc.set_defaults(control_exchange='sdn') topic = "sample2" to_host = "Client-2" class Server(proxy.RpcProxy): OPTS = [] BASE_RPC_API_VERSION = '1.0' def __init__(self): self.hostname = os.uname()[1] self.context = context.get_admin_context() super(Server, self).__init__( topic=topic, default_version=self.BASE_RPC_API_VERSION) eventlet.spawn(self._periodic_cast) def _periodic_cast(self): while True: time.sleep(1) self.func_cast() def func_cast(self): self.nowtime = datetime.datetime.today() LOG.debug('send cast [ hostname=%s, nowtime=%s ]' %(self.hostname, self.nowtime)) method = "func_cast" self.cast( self.context, self.make_msg( method, hostname=self.hostname, nowtime=self.nowtime ), topic = "%s.%s" % (topic, to_host)) if __name__ == '__main__': Server() while True: time.sleep(1)
クライアント側サンプルコード"receive_cast2.py"
import os import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['client.conf']) rpc.set_defaults(control_exchange='sdn') topic_base = "sample2" my_host = os.uname()[1] class Client(object): def __init__(self): topic = "%s.%s" % (topic_base, my_host) self.dispatcher = dispatcher.RpcDispatcher([self]) self.conn = rpc.create_connection(new=True) self.conn.create_consumer(topic, self.dispatcher) self.conn.consume_in_thread() def func_cast(self, context, **kwargs): LOG.debug('received cast [ hostname=%s, nowtime=%s ]' %(kwargs["hostname"],kwargs["nowtime"])) if __name__ == '__main__': Client() while True: time.sleep(1)
実行結果
サンプルプログラムの実行結果は、こんな感じ。(上段がサーバ装置、中段、下段がクライアント装置)
routing keyに”CLient-2”を含んだ通知メッセージを受信するたびに、上段のサーバ装置のマシン時刻がクライアント装置”Client-2”のみに特定通知されている様子が確認できた。
cast動作概要
サンプルプログラム実行結果に関わるcast動作部分を図式化してみた。
◆おわりに
今回は、Qpid基本動作として、特定通知モデルのcastメソッド活用方法が理解できた。
次回は、案3「情報照会モデル」を試してみるつもり。
OpenStackメッセージング機構を探ってみる(2) 〜Fanout(同報通知)編〜
前回、OpenStackメッセージング機構を探るための実験環境が構築できた。
OpenStackメッセージング機構を探ってみる(1) 〜環境構築編〜 - SDN開発エンジニアを目指した活動ブログ
今回から、OpenStack開発エンジニアを目指して、OpenStackメッセージング機構を体感してみたい。
◆はじめに
OpenStackメッセージング機構を体感するために、以下の例題を解決する実現モデルを考えてみる。
1. 例題
実験環境は、サーバ装置1台と、クライアント装置2台で構成されている。
ある特定のクライアント装置が、サーバ装置のマシン時刻を知りたい場合、どうような解決方法が存在するのだろうか?
2. 実現モデル
ここでは、OpenStackメッセージング機構の理解を目的とするので、実現モデルの優位性とかは問題視しません。
案1)同報通知モデル「定期的に、サーバ装置が、全クライアント装置に、同報的に通知する」
案2)特定通知モデル「定期的に、サーバ装置が、特定のクライアント装置に、通知する。」
案3)情報照会モデル「特定のクライアント装置が、サーバ装置にマシン時刻を問い合わせる」
◆案1「同報通知モデル」を試してみた
このモデルは、PublisherからConsumer宛に同報通知する仕組み。fanout_castというメソッドが用意されている。
サーバ側サンプルコード"send_fanout.py"
import os import datetime import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import proxy from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['server.conf']) topic = "sample1" my_host = os.uname()[1] class Server(proxy.RpcProxy): OPTS = [] BASE_RPC_API_VERSION = '1.0' def __init__(self): self.context = context.get_admin_context() super(Server, self).__init__( topic=topic, default_version=self.BASE_RPC_API_VERSION) eventlet.spawn(self._periodic_fanout) def _periodic_fanout(self): while True: time.sleep(1) self.func_fanout() def func_fanout(self): self.nowtime = datetime.datetime.today() LOG.debug('send fanout [ hostname=%s, nowtime=%s ]' %(my_host, self.nowtime)) method = "func_fanout" self.fanout_cast( self.context, self.make_msg( method, hostname=my_host, nowtime=self.nowtime ), topic=self.topic) if __name__ == '__main__': Server() while True: time.sleep(1)
サーバ側の設定ファイル"server.conf"
[DEFAULT] qpid_hostname = 192.168.100.101 rpc_backend = openstack.common.rpc.impl_qpid
クライアント側サンプルコード"receive_fanout.py"
import time import logging import eventlet eventlet.monkey_patch() from openstack.common import context from openstack.common import rpc from openstack.common.rpc import dispatcher from oslo.config import cfg logging.basicConfig() logging.getLogger('qpid').setLevel(logging.INFO) logging.getLogger('amqp').setLevel(logging.INFO) logging.getLogger('openstack.common').setLevel(logging.INFO) LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) CONF = cfg.CONF CONF(default_config_files=['client.conf']) topic = "sample1" class Client(object): OPTS = [] def __init__(self): self.context = context.get_admin_context() self.dispatcher = dispatcher.RpcDispatcher([self]) self.conn = rpc.create_connection(new=True) self.conn.create_consumer(topic, self.dispatcher, fanout=True) self.conn.consume_in_thread() def func_fanout(self, ctxt, **kwargs): LOG.debug('received fanout [ hostname=%s, nowtime=%s ]' %(kwargs["hostname"],kwargs["nowtime"])) if __name__ == '__main__': Client() while True: time.sleep(1)
クライアント側の設定ファイル"client.conf"
[DEFAULT] qpid_hostname = 192.168.100.101 rpc_backend = openstack.common.rpc.impl_qpid
実行結果
サンプルプログラムの実行結果は、こんな感じ。(上段がサーバ装置、中段、下段がクライアント装置)
約1秒ごとに、上段のサーバ装置のマシン時刻が各クライアント装置に同報通知されている様子が確認できた。
fanout動作概要
サンプルプログラム実行結果に関わるfanout動作部分を図式化してみた。
実際、qpid-toolsを活用すると、Qpid動作情報も、いろいろと確認できて、Good !
◆おわりに
今回は、Qpid基本動作として、同報通知モデルのfanoutメソッド活用方法が理解できた。
OpenStack基盤での開発方法は、なかなか参考文献が少なく、かなり手探りなところがあるが、実際に動作できたので結果オーライでしょうか。
次回は、案2「特定通知モデル」を試してみるつもり。
OpenStackメッセージング機構を探ってみる(1) 〜環境構築編〜
今回から、OpenStackで活用されているメッセージング機構を調べてみる。
以前のOpenStackユーザ会での講演資料「OpenStack を支える メッセージングの基礎」あたりの理解から始めてみる。
http://openstack.jp/assets/files/20121216/osc2012cloudjosugamqpv2-121216085708-phpapp02.pdf
◆構築手順メモ
動作原理を理解するには、実際に試してみるのがいちばんの近道なので、まずは環境構築から ...
(1)サーバ側:
・OS: Ubuntu 14.04 Server版
・AMQP:Apache Qpid
・oslo-incubator環境
(2)クライアント側:
・OS: Ubuntu 14.04 Server版
・oslo-incubator環境
(1) オペレーティングシステム環境
Qpidを動作される場合には、RHEL系のCentOSが望ましいところ。
しかしながら、Python2.7を使いたいので、あえて、Ubuntuを選択した。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 14.04 LTS Release: 14.04 Codename: trusty $ uname -a Linux Client 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
(2) Apache Qpid環境
Apache Qpidには管理ツールとして、qpid-toolsもインストールしておきたいところ。
qpid-toolsのインストールには、パッケージの依存関係を考慮して手作業で実施した。なお、ツール使用方法は、こちらがブログ記事を参考にさせて頂いた。
OSSはアルミニウムの翼で飛ぶ: apache qpid - exchange/queue/bindの監視
$ sudo apt-get install qpidd $ sudo -i # dpkg -i libboost-program-options1.46.1_1.46.1-7ubuntu3_amd64.deb # dpkg -i libqmf2-1_0.16-7ubuntu1_amd64.deb # dpkg -i libqmfconsole2_0.14-2_amd64.deb # dpkg -i libqpidmessaging2_0.16-7ubuntu1_amd64.deb # dpkg -i libqpidclient2_0.14-2_amd64.deb # dpkg -i libqmf2-1_0.16-7ubuntu1_amd64.deb # dpkg -i python-cqmf2_0.14-2_amd64.deb # dpkg -i python-qmf2_0.14-2_amd64.deb # dpkg -i python-setuptools_3.3-1ubuntu1_all.deb # dpkg -i python-qpid_0.12-2_all.deb # dpkg -i python-qpid-extras-qmf_0.12-1_all.deb # dpkg -i qpid-tools_0.12-1_all.deb
(3) Qpid設定見直し
今回はあくまでも、動作原理のお勉強を目的とするので、Qpid認証は無効にしておいた。
$ sudo cat /etc/qpid/qpidd.conf |grep auth= auth=no $ sudo service qpidd stop * Stopping AMQP broker qpidd [ OK ] $ sudo service qpidd start * Starting AMQP broker qpidd [ OK ]
(4) Qpid用サンプルプログラムの置き場所
qpid-toolsをインストールすると、サンプルプログラムも利用できるようになる。
# cd /usr/share/doc/python-qpid/examples/api # ls -l total 20 -rwxr-xr-x 1 root root 3285 Jun 10 2010 drain -rwxr-xr-x 1 root root 1353 Aug 10 2010 hello -rwxr-xr-x 1 root root 2121 Aug 10 2010 hello_xml -rwxr-xr-x 1 root root 3044 Jun 10 2010 server -rwxr-xr-x 1 root root 4004 Jul 14 2011 spout
(5) OpenStackメッセージング環境の構築
本来ならば、OpenStackのインストールから取りかかるべきところだが、手間と労力削減のため、今回は、oslo-incubator環境で構築してみた。
oslo-incubator自体は、Githubで公開されているので、インストールを手軽に試せる点が Good !!
openstack/oslo-incubator · GitHub
$ sudo apt-get install git $ sudo apt-get install python-pip $ git clone https://github.com/openstack/oslo-incubator.git Cloning into 'oslo-incubator'... remote: Reusing existing pack: 16413, done. remote: Counting objects: 78, done. remote: Compressing objects: 100% (72/72), done. remote: Total 16491 (delta 25), reused 18 (delta 5) Receiving objects: 100% (16491/16491), 4.58 MiB | 548.00 KiB/s, done. Resolving deltas: 100% (10067/10067), done. Checking connectivity... done. $ cd oslo-incubator $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/stable/havana remotes/origin/stable/icehouse $ git checkout -b stable/icehouse remotes/origin/stable/icehouse $ git branch master * stable/icehouse $ sudo apt-get install python-dev $ sudo pip install -r requirements.txt $ sudo python ./setup.py install
◆おわりに
検証環境自体は、結構手軽に構築できた。
次回からは、本格的にメッセージング機構の理解を着手したい。
Python勉強メモ:特殊メソッド__getattribute__とは?
Pythonを勉強しているなかで、特殊メソッド__getattribute__に遭遇したので、ちょっと調べてみた。
どうやら、__getattribute__のほかにも、__getattr__なるものがあるらしい。
・__getattr__:未定義のメンバーにアクセスする際に呼び出される。
・__getattribute__:未定義・定義済み関わらず、すべてのメンバーアクセスで呼び出される。
◆特殊メソッド __getattribute__
まずは、__getattribute__サンプルとして、test_getattribute.py で試してみた。
import sys class A(object): def __init__(self): print "aaa0" def a1(self): print "aaa1" class B(A): def __init__(self): print "bbb0" def b1(self): print "bbb1" def __getattribute__(self,name): try: print "getattribute:name=%s"%name return object.__getattribute__(self, name) except: print "undefined method" sys.exit() if __name__ == '__main__': t1=B() t1.b1() print "------" t2=B() t2.a1() print "------" t3=B() t3.c1()
test_getattribute.py を動かしてみた。
$ python test_getattribute.py bbb0 getattribute:name=b1 bbb1 ------ bbb0 getattribute:name=a1 aaa1 ------ bbb0 getattribute:name=c1 undefined method
継承クラス関係のいずれのメソッドにアクセスした場合でも、必ず、__getattribute__が呼び出されているし、
さらに、未定義メソッドにアクセスした場合でも、__getattribute__が呼び出されている様子がわかると思います。
◆特殊メソッド __getattr__
つぎに、__getattr__サンプルとして、test_getattr.py で試してみた。
import sys class A(object): def __init__(self): print "aaa0" def a1(self): print "aaa1" class B(A): def __init__(self): print "bbb0" def b1(self): print "bbb1" def __getattr__(self,name): try: print "getattr:name=%s"%name return object.__getattr__(self, name) except: print "undefined method" sys.exit() if __name__ == '__main__': t1=B() t1.b1() print "------" t2=B() t2.a1() print "------" t3=B() t3.c1()
test_getattr.py を動かしてみた。
$ python test_getattr.py bbb0 bbb1 ------ bbb0 aaa1 ------ bbb0 getattr:name=c1 undefined method
単に、未定義メソッドにアクセスした場合に限り、__getattr__が呼び出されている様子がわかると思います。
◆おわりに
継承クラス関係における子クラスで、__getattribute__を定義しておけば、いずれのメソッドへのアクセスが発生したのかを一元的に観測できるようになる。確かに便利そうだと理解できた。