SDN開発エンジニアを目指した活動ブログ

〜SDNなオープンソース製品を実際に使って試してみる〜

Ryu SDN FrameworkのBGP機能を試してみた(2) 〜InterAS接続編 その1〜

前回は、BGP/MPLS VPN網のエッジルータ適用でのコントロールプレーンの挙動を確認しました。しかし、実際にエッジルータ適用を想定した場合には、OSPFルーティングドメインへの参加や、トンネルMPLSラベル制御を含めたデータプレーン連携の実装が必要不可欠となります。現段階では、Ryu BGP実装状況での対応は困難そうです。よって、別の方式を検討してみます。

f:id:ttsubo:20140823171252j:plain

今回は、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エミュレータソフトを活用しました。
f:id:ttsubo:20140823171312j:plain

(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テーブルが正しく構築されることを確認します。
f:id:ttsubo:20140823175303j:plain

(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テーブルが正しく構築されることを確認します。
f:id:ttsubo:20140823181549j:plain

(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テーブルが正しく構築されることを確認します。
f:id:ttsubo:20140823181619j:plain

(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

◆おわりに

今回、InterAS MPLS VPN構成におけるBGP経路選定が正しく動作していることが確認できました。この検証モデルの特徴としては、前回と比べてBGP/MPLS VPN網内のOSPFルーティング制御およびトンネルMPLSラベル制御に全く関与せずとも、既存BGP/MPLS VPN上のVRFテーブルとルーティング共有が図れることです。なお、今回は、RyuBGPを2台構成としましたが、さらなるRyuBGP追加を行う場合は、BGPフルメッシュ構成を含めたトポロジ拡張を考慮すべきです。次回は、このトピックを扱いたいと思います。

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分離に関わる通信プロトコル基本動作原理の解説については、ブログ記事の領域を超越してしまうため、割愛させて頂きます。この技術分野に興味を持たれる方は、こちらの文献が一読されることを、お勧め致します。

インターネットルーティング入門 第3版

インターネットルーティング入門 第3版

昔のインターネット普及以前では、企業ユーザの各拠点間を専用線などの占有タイプのネットワークサービスを活用して相互接続を図っておりました。その後のインターネット技術の台頭あたりから、拠点間の相互接続を通信キャリアが運営する閉域ネットワークに重畳することにより、通信コストの低減を図りつつ、安定的なネットワーク品質の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を活用してオートスケールなアクティベーションを可能とする技術となります。

f:id:ttsubo:20140815223159j:plain
拠点間での相互接続およびテナント分離に関わる基本動作は、概ねエッジルータ(PE)で行うことになります。
さらに、拠点間でのL3経路情報の伝搬および拠点間の通信経路選定に関わる通信制御系の動作と拠点間のデータ転送系の動作は、明示的に分離されている特徴があります。昨今のSDNによるC-Plane/D-Plane分離技術に通じるものです。

f:id:ttsubo:20140815223700j:plain

◆Ryu BGP検証環境

BGP/MPLS VPN網ではエッジルータの役割が非常に重要ということになりますので、Ryu BGP機能のエッジルータ適用を想定した検証モデルを構築します。実際のBGP/MPLS VPN網は、GNS3によるCiscoエミュレータソフトを活用しました。
f:id:ttsubo:20140815224625j:plain

1. 制御系ネットワーク
本来のBGP/MPLS VPN網でのエッジルータでは、エッジルータ間の相互接続としてOSPF, LDPなどのトンネル・ルーティング制御が必要不可欠となるわけですが、今回は、この技術領域の検証は行いません。ただし、BGPプロトコルでは、NextHop指定として、Ryu BGP〜各PE間での到達性が必要となるため、Ryu BGPサーバも各PE間で構築されたOSPFルーティング面に参加される必要がありました。今回は、Ryu BGPサーバ自体でOSPFプロトコルを扱うことはせず、Ryu BGP〜RR間をパッシブインターフェース区間として扱いました。
f:id:ttsubo:20140815224436j:plain

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.

f:id:ttsubo:20140615061521p:plain

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)

実行結果

サンプルプログラムの実行結果は、こんな感じ。(上段がサーバ装置、下段がクライアント装置)
クライアント装置からサーバ装置のマシン時刻の問い合わせを行うたびに、情報照会されている様子が確認できた。
f:id:ttsubo:20140615064258j:plain

call動作概要

サンプルプログラム実行結果に関わるcall動作部分を図式化してみた。
f:id:ttsubo:20140615064413j:plain

Qpid基本動作として、情報照会モデルのcallメソッド活用方法が理解できた。

◆おまけ編

これまで、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"への切り替えが簡単に実現できてしまう点が確認できた。
f:id:ttsubo:20140615072657j:plain

RabbitMQ Web管理インターフェイス

RabbitMQには、Web管理インターフェイスなるものが存在するらしい。そこで、実際に使ってみた。
第14章 ロギングと監視 - OpenStack 運用ガイド

$ sudo /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management
$ sudo service rabbitmq-server restart
(1) Overview

f:id:ttsubo:20140615081651p:plain

(2) Exchanges

f:id:ttsubo:20140615081822p:plain

(3) Queues

f:id:ttsubo:20140615081904p:plain

◆おわりに

実際に、サンプルプログラムを動かしながら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.

f:id:ttsubo:20140614094732p:plain

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台とも含まれている。これにより、サーバ装置が転送したマシン時刻を、クライアント装置の交互に通知される様子が確認できた。

f:id:ttsubo:20140614102318j:plain

cast動作概要

サンプルプログラム実行結果に関わるcast動作部分を図式化してみた。

f:id:ttsubo:20140614102502j:plain

◆案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”のみに特定通知されている様子が確認できた。

f:id:ttsubo:20140614142636j:plain

cast動作概要

サンプルプログラム実行結果に関わるcast動作部分を図式化してみた。

f:id:ttsubo:20140614142651j:plain

◆おわりに
今回は、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秒ごとに、上段のサーバ装置のマシン時刻が各クライアント装置に同報通知されている様子が確認できた。
f:id:ttsubo:20140614092914j:plain

fanout動作概要

サンプルプログラム実行結果に関わるfanout動作部分を図式化してみた。
実際、qpid-toolsを活用すると、Qpid動作情報も、いろいろと確認できて、Good !
f:id:ttsubo:20140614092901j:plain

◆おわりに

今回は、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__を定義しておけば、いずれのメソッドへのアクセスが発生したのかを一元的に観測できるようになる。確かに便利そうだと理解できた。