# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import sys
import time
import unittest
import inspect

import collections
collections.Callable = collections.abc.Callable

from lib.noseplugin import parser_option

from lib import base
from lib.base import (
    Bridge,
    BGP_FSM_ESTABLISHED,
    BGP_ATTR_TYPE_COMMUNITIES,
    BGP_ATTR_TYPE_EXTENDED_COMMUNITIES,
    local,
)
from lib.gobgp import GoBGPContainer
from lib.quagga import QuaggaBGPContainer
from lib.exabgp import ExaBGPContainer


counter = 1
_SCENARIOS = {}


def register_scenario(cls):
    global counter
    _SCENARIOS[counter] = cls
    counter += 1


def lookup_scenario(name):
    for value in list(_SCENARIOS.values()):
        if value.__name__ == name:
            return value
    return None


def wait_for(f, timeout=120):
    interval = 1
    count = 0
    while True:
        if f():
            return

        time.sleep(interval)
        count += interval
        if count >= timeout:
            raise Exception('timeout')


@register_scenario
class ImportPolicy(object):
    """
    No.1 import-policy test
                            --------------------------------
    e1 ->(192.168.2.0/24)-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                            |                              |
                            | ->x q2-rib                   |
                            --------------------------------
    """
    @staticmethod
    def boot(env):
        gobgp_ctn_image_name = env.parser_option.gobgp_image
        log_level = env.parser_option.gobgp_log_level
        g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1',
                            ctn_image_name=gobgp_ctn_image_name,
                            log_level=log_level)
        e1 = ExaBGPContainer(name='e1', asn=65001, router_id='192.168.0.2')
        q1 = QuaggaBGPContainer(name='q1', asn=65002, router_id='192.168.0.3')
        q2 = QuaggaBGPContainer(name='q2', asn=65003, router_id='192.168.0.4')

        ctns = [g1, e1, q1, q2]
        initial_wait_time = max(ctn.run() for ctn in ctns)
        time.sleep(initial_wait_time)

        for q in [e1, q1, q2]:
            g1.add_peer(q, is_rs_client=True)
            q.add_peer(g1)

        env.g1 = g1
        env.e1 = e1
        env.q1 = q1
        env.q2 = q2

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.0.0/16 16..24')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked
        e1.add_route('192.168.2.0/24')
        # this will pass
        e1.add_route('192.168.2.0/15')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1)) == 2)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1)) == 2)
        wait_for(lambda: len(env.q1.get_global_rib()) == 2)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2)) == 2)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2)) == 1)
        wait_for(lambda: len(env.q2.get_global_rib()) == 1)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicy").boot(env)
        lookup_scenario("ImportPolicy").setup(env)
        lookup_scenario("ImportPolicy").check(env)


@register_scenario
class ExportPolicy(object):
    """
    No.2 export-policy test
                            --------------------------------
    e1 ->(192.168.2.0/24)-> | -> q1-rib ->  q1-adj-rib-out | --> q1
                            |                              |
                            | -> q2-rib ->x q2-adj-rib-out |
                            --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.0.0/16 16..24')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked
        e1.add_route('192.168.2.0/24')
        # this will pass
        e1.add_route('192.168.2.0/15')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 2)
        wait_for(lambda: len(q1.get_global_rib()) == 2)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 1)
        wait_for(lambda: len(q2.get_global_rib()) == 1)

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicy").boot(env)
        lookup_scenario("ExportPolicy").setup(env)
        lookup_scenario("ExportPolicy").check(env)


@register_scenario
class ImportPolicyUpdate(object):
    """
    No.3 import-policy update test

    r1:192.168.2.0/24
    r2:192.168.20.0/24
    r3:192.168.200.0/24
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1)->       rib ->(r1)->       adj-rib-out | ->(r1)-> q2
                      -------------------------------------------------
                 |
         update gobgp.conf
                 |
                 V
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r3)->    rib ->(r1,r3)->    adj-rib-out | ->(r1,r3)-> q2
                      -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.20.0/24')
        g1.local('gobgp policy prefix add ps0 192.168.200.0/24')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.2.0/24')
        e1.add_route('192.168.20.0/24')
        e1.add_route('192.168.200.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 3)
        wait_for(lambda: len(q1.get_global_rib()) == 3)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 1)
        wait_for(lambda: len(q2.get_global_rib()) == 1)

    @staticmethod
    def setup2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix del ps0 192.168.200.0/24')
        g1.reset(e1)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 3)
        wait_for(lambda: len(q1.get_global_rib()) == 3)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 2)
        wait_for(lambda: len(q2.get_global_rib()) == 2)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyUpdate").boot(env)
        lookup_scenario("ImportPolicyUpdate").setup(env)
        lookup_scenario("ImportPolicyUpdate").check(env)
        lookup_scenario("ImportPolicyUpdate").setup2(env)
        lookup_scenario("ImportPolicyUpdate").check2(env)


@register_scenario
class ExportPolicyUpdate(object):
    """
    No.4 export-policy update test

    r1:192.168.2.0
    r2:192.168.20.0
    r3:192.168.200.0
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r2,r3)-> rib ->(r1)->       adj-rib-out | ->(r1)-> q2
                      -------------------------------------------------
                 |
         update gobgp.conf
                 |
                 V
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r2,r3)-> rib ->(r1,r3)->    adj-rib-out | ->(r1,r3)-> q2
                      -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.20.0/24')
        g1.local('gobgp policy prefix add ps0 192.168.200.0/24')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.2.0/24')
        e1.add_route('192.168.20.0/24')
        e1.add_route('192.168.200.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 3)
        wait_for(lambda: len(q1.get_global_rib()) == 3)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 1)
        wait_for(lambda: len(q2.get_global_rib()) == 1)

    @staticmethod
    def setup2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix del ps0 192.168.200.0/24')
        # we need hard reset to flush q2's local rib
        g1.reset(e1)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 3)
        wait_for(lambda: len(q1.get_global_rib()) == 3)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 2)
        wait_for(lambda: len(q2.get_global_rib()) == 2)

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyUpdate").boot(env)
        lookup_scenario("ExportPolicyUpdate").setup(env)
        lookup_scenario("ExportPolicyUpdate").check(env)
        lookup_scenario("ExportPolicyUpdate").setup2(env)
        lookup_scenario("ExportPolicyUpdate").check2(env)


@register_scenario
class ExportPolicyUpdateRouteRefresh(object):
    """
    export-policy update and route refresh handling test

    r1:192.168.2.0
    r2:192.168.20.0
    r3:192.168.200.0
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r2,r3)-> rib ->(r1)->       adj-rib-out | ->(r1)-> q2
                      -------------------------------------------------
                 |
         update gobgp.conf
         q2 sends route refresh
                 |
                 V
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r2,r3)-> rib ->(r1,r3)->    adj-rib-out | ->(r1,r3)-> q2
                      -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ExportPolicyUpdate").boot(env)

    @staticmethod
    def setup(env):
        lookup_scenario("ExportPolicyUpdate").setup(env)

    @staticmethod
    def check(env):
        lookup_scenario("ExportPolicyUpdate").check(env)

    @staticmethod
    def setup2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix del ps0 192.168.200.0/24')
        q2.send_route_refresh()

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check2(env):
        lookup_scenario("ExportPolicyUpdate").check2(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyUpdateRouteRefresh").boot(env)
        lookup_scenario("ExportPolicyUpdateRouteRefresh").setup(env)
        lookup_scenario("ExportPolicyUpdateRouteRefresh").check(env)
        lookup_scenario("ExportPolicyUpdateRouteRefresh").setup2(env)
        lookup_scenario("ExportPolicyUpdateRouteRefresh").check2(env)


@register_scenario
class ImportPolicyIPV6(object):
    """
    No.5 IPv6 import-policy test

    r1=2001::/64
    r2=2001::/63
                   -------------------------------------------------
    e1 ->(r1,r2)-> | ->(r1,r2)-> q1-rib ->(r1,r2)-> q1-adj-rib-out | ->(r1,r2)-> q1
                   |                                               |
                   | ->(r2)   -> q2-rib ->(r2)   -> q2-adj-rib-out | ->(r2)-> q2
                   -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        gobgp_ctn_image_name = env.parser_option.gobgp_image
        log_level = env.parser_option.gobgp_log_level
        g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1',
                            ctn_image_name=gobgp_ctn_image_name,
                            log_level=log_level)
        e1 = ExaBGPContainer(name='e1', asn=65001, router_id='192.168.0.2')
        q1 = QuaggaBGPContainer(name='q1', asn=65002, router_id='192.168.0.3')
        q2 = QuaggaBGPContainer(name='q2', asn=65003, router_id='192.168.0.4')

        ctns = [g1, e1, q1, q2]
        initial_wait_time = max(ctn.run() for ctn in ctns)
        time.sleep(initial_wait_time)

        br01 = Bridge(name='br01', subnet='2001::/96')
        [br01.addif(ctn) for ctn in ctns]

        for q in [e1, q1, q2]:
            g1.add_peer(q, is_rs_client=True, bridge=br01.name, v6=True)
            q.add_peer(g1, bridge=br01.name, v6=True)

            env.g1 = g1
            env.e1 = e1
            env.q1 = q1
            env.q2 = q2

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 2001::/32 64..128')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked
        e1.add_route('2001::/64', rf='ipv6')
        # this will pass
        e1.add_route('2001::/63', rf='ipv6')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1, rf='ipv6')) == 2)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1, rf='ipv6')) == 2)
        wait_for(lambda: len(env.q1.get_global_rib(rf='ipv6')) == 2)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2, rf='ipv6')) == 2)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2, rf='ipv6')) == 1)
        wait_for(lambda: len(env.q2.get_global_rib(rf='ipv6')) == 1)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyIPV6").boot(env)
        lookup_scenario("ImportPolicyIPV6").setup(env)
        lookup_scenario("ImportPolicyIPV6").check(env)


@register_scenario
class ExportPolicyIPV6(object):
    """
    No.6 IPv6 export-policy test

    r1=2001::/64
    r2=2001::/63
                   -------------------------------------------------
    e1 ->(r1,r2)-> | ->(r1,r2)-> q1-rib ->(r1,r2)-> q1-adj-rib-out | ->(r1,r2)-> q1
                   |                                               |
                   | ->(r1,r2)-> q2-rib ->(r2)   -> q2-adj-rib-out | ->(r2)-> q2
                   -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicyIPV6').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 2001::/32 64..128')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked
        e1.add_route('2001::/64', rf='ipv6')
        # this will pass
        e1.add_route('2001::/63', rf='ipv6')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1, rf='ipv6')) == 2)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1, rf='ipv6')) == 2)
        wait_for(lambda: len(env.q1.get_global_rib(rf='ipv6')) == 2)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2, rf='ipv6')) == 2)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2, rf='ipv6')) == 1)
        wait_for(lambda: len(env.q2.get_global_rib(rf='ipv6')) == 1)

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyIPV6").boot(env)
        lookup_scenario("ExportPolicyIPV6").setup(env)
        lookup_scenario("ExportPolicyIPV6").check(env)


@register_scenario
class ImportPolicyIPV6Update(object):
    """
    No.7 IPv6 import-policy update test
    r1=2001:0:10:2::/64
    r2=2001:0:10:20::/64
    r3=2001:0:10:200::/64
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1)->       rib ->(r1)->       adj-rib-out | ->(r1)-> q2
                      -------------------------------------------------
                 |
         update gobgp.conf
                 |
                 V
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r3)->    rib ->(r1,r3)->    adj-rib-out | ->(r1,r3)-> q2
                      -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicyIPV6').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 2001:0:10:2::/64')
        g1.local('gobgp policy prefix add ps0 2001:0:10:20::/64')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('2001:0:10:2::/64', rf='ipv6')
        e1.add_route('2001:0:10:20::/64', rf='ipv6')
        e1.add_route('2001:0:10:200::/64', rf='ipv6')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.q1.get_global_rib(rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2, rf='ipv6')) == 1)
        wait_for(lambda: len(env.q2.get_global_rib(rf='ipv6')) == 1)

    @staticmethod
    def setup2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix del ps0 2001:0:10:20::/64')
        g1.reset(e1)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check2(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.q1.get_global_rib(rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2, rf='ipv6')) == 2)
        wait_for(lambda: len(env.q2.get_global_rib(rf='ipv6')) == 2)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyIPV6Update").boot(env)
        lookup_scenario("ImportPolicyIPV6Update").setup(env)
        lookup_scenario("ImportPolicyIPV6Update").check(env)
        lookup_scenario("ImportPolicyIPV6Update").setup2(env)
        lookup_scenario("ImportPolicyIPV6Update").check2(env)


@register_scenario
class ExportPolicyIPv6Update(object):
    """
    No.8 IPv6 export-policy update test
    r1=2001:0:10:2::/64
    r2=2001:0:10:20::/64
    r3=2001:0:10:200::/64
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r2,r3)-> rib ->(r1)->       adj-rib-out | ->(r1)-> q2
                      -------------------------------------------------
                 |
         update gobgp.conf
                 |
                 V
                      -------------------------------------------------
                      | q1                                            |
    e1 ->(r1,r2,r3)-> | ->(r1,r2,r3)-> rib ->(r1,r2,r3)-> adj-rib-out | ->(r1,r2,r3)-> q1
                      |                                               |
                      | q2                                            |
                      | ->(r1,r2,r3)-> rib ->(r1,r3)->    adj-rib-out | ->(r1,r3)-> q2
                      -------------------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicyIPV6').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 2001:0:10:2::/64')
        g1.local('gobgp policy prefix add ps0 2001:0:10:20::/64')
        g1.local('gobgp policy neighbor add ns0 {0}'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add condition neighbor ns0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('2001:0:10:2::/64', rf='ipv6')
        e1.add_route('2001:0:10:20::/64', rf='ipv6')
        e1.add_route('2001:0:10:200::/64', rf='ipv6')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.q1.get_global_rib(rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2, rf='ipv6')) == 1)
        wait_for(lambda: len(env.q2.get_global_rib(rf='ipv6')) == 1)

    @staticmethod
    def setup2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix del ps0 2001:0:10:20::/64')
        g1.reset(e1)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check2(env):
        wait_for(lambda: len(env.g1.get_local_rib(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q1, rf='ipv6')) == 3)
        wait_for(lambda: len(env.q1.get_global_rib(rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_local_rib(env.q2, rf='ipv6')) == 3)
        wait_for(lambda: len(env.g1.get_adj_rib_out(env.q2, rf='ipv6')) == 2)
        wait_for(lambda: len(env.q2.get_global_rib(rf='ipv6')) == 2)

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyIPv6Update").boot(env)
        lookup_scenario("ExportPolicyIPv6Update").setup(env)
        lookup_scenario("ExportPolicyIPv6Update").check(env)
        lookup_scenario("ExportPolicyIPv6Update").setup2(env)
        lookup_scenario("ExportPolicyIPv6Update").check2(env)


@register_scenario
class ImportPolicyAsPathLengthCondition(object):
    """
    No.9 aspath length condition import-policy test
                              --------------------------------
    e1 ->(aspath_length=10)-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                              |                              |
                              | ->x q2-rib                   |
                              --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition as-path-length 10 ge')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked (length 10, >= 10)
        e1.add_route('192.168.100.0/24', aspath=[e1.asn] + list(range(65100, 65109)))
        # this will pass (length 8, < 10)
        e1.add_route('192.168.200.0/24', aspath=[e1.asn] + list(range(65100, 65107)))

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 2)
        wait_for(lambda: len(q1.get_global_rib()) == 2)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 1)
        wait_for(lambda: len(q2.get_global_rib()) == 1)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathLengthCondition").boot(env)
        lookup_scenario("ImportPolicyAsPathLengthCondition").setup(env)
        lookup_scenario("ImportPolicyAsPathLengthCondition").check(env)


@register_scenario
class ImportPolicyAsPathCondition(object):
    """
    No.10 aspath from condition import-policy test
                                --------------------------------
    e1 ->(aspath=[65100,...])-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                |                              |
                                | ->x q2-rib                   |
                                --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy as-path add as0 ^{0}'.format(e1.asn))
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition as-path as0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked (starts with e1.asn, matches ^65001)
        e1.add_route('192.168.100.0/24', aspath=[e1.asn] + list(range(65100, 65109)))
        # this will pass (does not start with e1.asn)
        e1.add_route('192.168.200.0/24', aspath=list(range(65100, 65109)))

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        # same check function as previous No.1 scenario
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathCondition").boot(env)
        lookup_scenario("ImportPolicyAsPathCondition").setup(env)
        lookup_scenario("ImportPolicyAsPathCondition").check(env)


@register_scenario
class ImportPolicyAsPathAnyCondition(object):
    """
    No.11 aspath any condition import-policy test
                                   --------------------------------
    e1 ->(aspath=[...65098,...])-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                   |                              |
                                   | ->x q2-rib                   |
                                   --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy as-path add as0 65098')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition as-path as0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked (contains 65098)
        e1.add_route('192.168.100.0/24', aspath=[65001, 65098, 65010])
        # this will pass (does not contain 65098)
        e1.add_route('192.168.200.0/24', aspath=[65001, 65100, 65010])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        # same check function as previous No.1 scenario
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathAnyCondition").boot(env)
        lookup_scenario("ImportPolicyAsPathAnyCondition").setup(env)
        lookup_scenario("ImportPolicyAsPathAnyCondition").check(env)


@register_scenario
class ImportPolicyAsPathOriginCondition(object):
    """
    No.12 aspath origin condition import-policy test
                                --------------------------------
    e1 ->(aspath=[...,65090])-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                |                              |
                                | ->x q2-rib                   |
                                --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy as-path add as0 65090$')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition as-path as0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked (ends with 65090, matches 65090$)
        e1.add_route('192.168.100.0/24', aspath=[65001, 65098, 65090])
        # this will pass (does not end with 65090)
        e1.add_route('192.168.200.0/24', aspath=[65001, 65100, 65010])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        # same check function as previous No.1 scenario
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathOriginCondition").boot(env)
        lookup_scenario("ImportPolicyAsPathOriginCondition").setup(env)
        lookup_scenario("ImportPolicyAsPathOriginCondition").check(env)


@register_scenario
class ImportPolicyAsPathOnlyCondition(object):
    """
    No.13 aspath only condition import-policy test
                              --------------------------------
    e1 -> (aspath=[65100]) -> | ->  q1-rib -> q1-adj-rib-out | --> q1
                              |                              |
                              | ->x q2-rib                   |
                              --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy as-path add as0 ^65100$')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition as-path as0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked (exactly matches ^65100$)
        e1.add_route('192.168.100.0/24', aspath=[65100])
        # this will pass (does not match ^65100$)
        e1.add_route('192.168.200.0/24', aspath=[65001, 65100, 65010])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        # same check function as previous No.1 scenario
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathOnlyCondition").boot(env)
        lookup_scenario("ImportPolicyAsPathOnlyCondition").setup(env)
        lookup_scenario("ImportPolicyAsPathOnlyCondition").check(env)


@register_scenario
class ImportPolicyAsPathMismatchCondition(object):
    """
    No.14 aspath condition mismatch import-policy test
                                   -------------------------------
    exabgp ->(aspath=[...,65090])->| -> q1-rib -> q1-adj-rib-out | --> q1
                                   |                             |
                                   | -> q2-rib -> q2-adj-rib-out | --> q2
                                   -------------------------------
    This case check if policy passes the path to e1 because of condition mismatch.
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked (no community match, but condition mismatch means it passes)
        e1.add_route('192.168.100.0/24', aspath=[65100, 65090])
        # this will pass (no community match)
        e1.add_route('192.168.200.0/24', aspath=[65001, 65100, 65010])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 2)
        wait_for(lambda: len(q1.get_global_rib()) == 2)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 2)
        wait_for(lambda: len(q2.get_global_rib()) == 2)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathMismatchCondition").boot(env)
        lookup_scenario("ImportPolicyAsPathMismatchCondition").setup(env)
        lookup_scenario("ImportPolicyAsPathMismatchCondition").check(env)


@register_scenario
class ImportPolicyCommunityCondition(object):
    """
    No.15 community condition import-policy test
                                --------------------------------
    e1 ->(community=65100:10)-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                |                              |
                                | ->x q2-rib                   |
                                --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked
        e1.add_route('192.168.100.0/24', community=['65100:10'])
        # this will pass
        e1.add_route('192.168.200.0/24', community=['65100:20'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyCommunityCondition").boot(env)
        lookup_scenario("ImportPolicyCommunityCondition").setup(env)
        lookup_scenario("ImportPolicyCommunityCondition").check(env)


@register_scenario
class ImportPolicyCommunityRegexp(object):
    """
    No.16 community condition regexp import-policy test
                                --------------------------------
    e1 ->(community=65100:10)-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                |                              |
                                | ->x q2-rib                   |
                                --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        g1.local('gobgp policy community add cs0 6[0-9]+:[0-9]+')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        # this will be blocked
        e1.add_route('192.168.100.0/24', community=['65100:10'])
        # this will pass
        e1.add_route('192.168.200.0/24', community=['55100:20'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyCommunityRegexp").boot(env)
        lookup_scenario("ImportPolicyCommunityRegexp").setup(env)
        lookup_scenario("ImportPolicyCommunityRegexp").check(env)


def community_exists(path, com):
    a, b = com.split(':')
    com = (int(a) << 16) + int(b)
    for a in path['attrs']:
        if a['type'] == BGP_ATTR_TYPE_COMMUNITIES and com in a['communities']:
            return True
    return False


@register_scenario
class ImportPolicyCommunityAction(object):
    """
    No.17 community add action import-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)->          q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=65100:10,65100:20)-> q2
                                |    apply action             |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community add 65100:20')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 1)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 1)
        wait_for(lambda: len(q1.get_global_rib()) == 1)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 1)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 1)
        wait_for(lambda: len(q2.get_global_rib()) == 1)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2
        path = g1.get_adj_rib_out(q1)[0]
        assert community_exists(path, '65100:10')
        assert not community_exists(path, '65100:20')
        path = g1.get_adj_rib_out(q2)[0]
        assert community_exists(path, '65100:10')
        assert community_exists(path, '65100:20')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyCommunityAction").boot(env)
        lookup_scenario("ImportPolicyCommunityAction").setup(env)
        lookup_scenario("ImportPolicyCommunityAction").check(env)
        lookup_scenario("ImportPolicyCommunityAction").check2(env)


@register_scenario
class ImportPolicyCommunityReplace(object):
    """
    No.18 community replace action import-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=65100:20)-> q2
                                |    apply action             |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community replace 65100:20')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        path = g1.get_adj_rib_out(q1)[0]
        assert community_exists(path, '65100:10')
        assert not community_exists(path, '65100:20')
        path = g1.get_adj_rib_out(q2)[0]
        assert not community_exists(path, '65100:10')
        assert community_exists(path, '65100:20')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyCommunityReplace").boot(env)
        lookup_scenario("ImportPolicyCommunityReplace").setup(env)
        lookup_scenario("ImportPolicyCommunityReplace").check(env)
        lookup_scenario("ImportPolicyCommunityReplace").check2(env)


@register_scenario
class ImportPolicyCommunityRemove(object):
    """
    No.19 community remove action import-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=null)->     q2
                                |    apply action             |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community remove 65100:10 65100:20')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10'])
        e1.add_route('192.168.110.0/24', community=['65100:10', '65100:20'])
        e1.add_route('192.168.120.0/24', community=['65100:10', '65100:30'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_local_rib(q1)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 3)
        wait_for(lambda: len(q1.get_global_rib()) == 3)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 3)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 3)
        wait_for(lambda: len(q2.get_global_rib()) == 3)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        adj_out = g1.get_adj_rib_out(q1)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            if path['nlri']['prefix'] == '192.168.110.0/24':
                assert community_exists(path, '65100:20')
            if path['nlri']['prefix'] == '192.168.120.0/24':
                assert community_exists(path, '65100:30')
        adj_out = g1.get_adj_rib_out(q2)
        for path in adj_out:
            assert not community_exists(path, '65100:10')
            if path['nlri']['prefix'] == '192.168.110.0/24':
                assert not community_exists(path, '65100:20')
            if path['nlri']['prefix'] == '192.168.120.0/24':
                assert community_exists(path, '65100:30')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyCommunityRemove").boot(env)
        lookup_scenario("ImportPolicyCommunityRemove").setup(env)
        lookup_scenario("ImportPolicyCommunityRemove").check(env)
        lookup_scenario("ImportPolicyCommunityRemove").check2(env)


@register_scenario
class ImportPolicyCommunityNull(object):
    """
    No.20 community null action import-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=null)->     q2
                                |    apply action             |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community replace')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10'])
        e1.add_route('192.168.110.0/24', community=['65100:10', '65100:20'])
        e1.add_route('192.168.120.0/24', community=['65100:10', '65100:30'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityRemove').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2
        adj_out = g1.get_adj_rib_out(q1)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            if path['nlri']['prefix'] == '192.168.110.0/24':
                assert community_exists(path, '65100:20')
            if path['nlri']['prefix'] == '192.168.120.0/24':
                assert community_exists(path, '65100:30')
        adj_out = g1.get_adj_rib_out(q2)
        for path in adj_out:
            assert not community_exists(path, '65100:10')
            if path['nlri']['prefix'] == '192.168.110.0/24':
                assert not community_exists(path, '65100:20')
            if path['nlri']['prefix'] == '192.168.120.0/24':
                assert not community_exists(path, '65100:30')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyCommunityNull").boot(env)
        lookup_scenario("ImportPolicyCommunityNull").setup(env)
        lookup_scenario("ImportPolicyCommunityNull").check(env)
        lookup_scenario("ImportPolicyCommunityNull").check2(env)


@register_scenario
class ExportPolicyCommunityAdd(object):
    """
    No.21 community add action export-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=null)->     q2
                                |              apply action   |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community add 65100:20')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            assert not community_exists(path, '65100:20')

        local_rib = g1.get_local_rib(q2)
        for path in local_rib[0]['paths']:
            assert community_exists(path, '65100:10')
            assert not community_exists(path, '65100:20')

        adj_out = g1.get_adj_rib_out(q2)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            assert community_exists(path, '65100:20')

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyCommunityAdd").boot(env)
        lookup_scenario("ExportPolicyCommunityAdd").setup(env)
        lookup_scenario("ExportPolicyCommunityAdd").check(env)
        lookup_scenario("ExportPolicyCommunityAdd").check2(env)


@register_scenario
class ExportPolicyCommunityReplace(object):
    """
    No.22 community replace action export-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=65100:20)-> q2
                                |              apply action   |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community replace 65100:20')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            assert not community_exists(path, '65100:20')

        local_rib = g1.get_local_rib(q2)
        for path in local_rib[0]['paths']:
            assert community_exists(path, '65100:10')
            assert not community_exists(path, '65100:20')

        adj_out = g1.get_adj_rib_out(q2)
        for path in adj_out:
            assert not community_exists(path, '65100:10')
            assert community_exists(path, '65100:20')

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyCommunityReplace").boot(env)
        lookup_scenario("ExportPolicyCommunityReplace").setup(env)
        lookup_scenario("ExportPolicyCommunityReplace").check(env)
        lookup_scenario("ExportPolicyCommunityReplace").check2(env)


@register_scenario
class ExportPolicyCommunityRemove(object):
    """
    No.23 community replace action export-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=null)->     q2
                                |              apply action   |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community remove 65100:20 65100:30')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10', '65100:20', '65100:30'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            assert community_exists(path, '65100:20')
            assert community_exists(path, '65100:30')

        local_rib = g1.get_local_rib(q2)
        for path in local_rib[0]['paths']:
            assert community_exists(path, '65100:10')
            assert community_exists(path, '65100:20')
            assert community_exists(path, '65100:30')

        adj_out = g1.get_adj_rib_out(q2)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            assert not community_exists(path, '65100:20')
            assert not community_exists(path, '65100:30')

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyCommunityRemove").boot(env)
        lookup_scenario("ExportPolicyCommunityRemove").setup(env)
        lookup_scenario("ExportPolicyCommunityRemove").check(env)
        lookup_scenario("ExportPolicyCommunityRemove").check2(env)


@register_scenario
class ExportPolicyCommunityNull(object):
    """
    No.24 community null action export-policy test
                                -------------------------------
    e1 ->(community=65100:10)-> | -> q1-rib -> q1-adj-rib-out | ->(community=65100:10)-> q1
                                |                             |
                                | -> q2-rib -> q2-adj-rib-out | ->(community=null)->     q2
                                |              apply action   |
                                -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy community add cs0 65100:10')
        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add condition community cs0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action community replace')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', community=['65100:10', '65100:20', '65100:30'])

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        for path in adj_out:
            assert community_exists(path, '65100:10')
            assert community_exists(path, '65100:20')
            assert community_exists(path, '65100:30')

        local_rib = g1.get_local_rib(q2)
        for path in local_rib[0]['paths']:
            assert community_exists(path, '65100:10')
            assert community_exists(path, '65100:20')
            assert community_exists(path, '65100:30')

        adj_out = g1.get_adj_rib_out(q2)
        for path in adj_out:
            assert not community_exists(path, '65100:10')
            assert not community_exists(path, '65100:20')
            assert not community_exists(path, '65100:30')

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyCommunityNull").boot(env)
        lookup_scenario("ExportPolicyCommunityNull").setup(env)
        lookup_scenario("ExportPolicyCommunityNull").check(env)
        lookup_scenario("ExportPolicyCommunityNull").check2(env)


def metric(path):
    for a in path['attrs']:
        if 'metric' in a:
            return a['metric']
    return -1


@register_scenario
class ImportPolicyMedReplace(object):
    """
    No.25 med replace action import-policy test
                     -------------------------------
    e1 ->(med=300)-> | -> q1-rib -> q1-adj-rib-out | ->(med=300)-> q1
                     |                             |
                     | -> q2-rib -> q2-adj-rib-out | ->(med=100)-> q2
                     |    apply action             |
                     -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action med set 100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', med=300)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        assert metric(adj_out[0]) == 300

        local_rib = g1.get_local_rib(q2)
        assert metric(local_rib[0]['paths'][0]) == 300

        adj_out = g1.get_adj_rib_out(q2)
        assert metric(adj_out[0]) == 100

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyMedReplace").boot(env)
        lookup_scenario("ImportPolicyMedReplace").setup(env)
        lookup_scenario("ImportPolicyMedReplace").check(env)
        lookup_scenario("ImportPolicyMedReplace").check2(env)


@register_scenario
class ImportPolicyMedAdd(object):
    """
    No.26 med add action import-policy test
                     -------------------------------
    e1 ->(med=300)-> | -> q1-rib -> q1-adj-rib-out | ->(med=300)->     q1
                     |                             |
                     | -> q2-rib -> q2-adj-rib-out | ->(med=300+100)-> q2
                     |    apply action             |
                     -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action med add 100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', med=300)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        assert metric(adj_out[0]) == 300

        local_rib = g1.get_local_rib(q2)
        assert metric(local_rib[0]['paths'][0]) == 300

        adj_out = g1.get_adj_rib_out(q2)
        assert metric(adj_out[0]) == 400

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyMedAdd").boot(env)
        lookup_scenario("ImportPolicyMedAdd").setup(env)
        lookup_scenario("ImportPolicyMedAdd").check(env)
        lookup_scenario("ImportPolicyMedAdd").check2(env)


@register_scenario
class ImportPolicyMedSub(object):
    """
    No.27 med subtract action import-policy test
                     -------------------------------
    e1 ->(med=300)-> | -> q1-rib -> q1-adj-rib-out | ->(med=300)->     q1
                     |                             |
                     | -> q2-rib -> q2-adj-rib-out | ->(med=300-100)-> q2
                     |    apply action             |
                     -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action med sub 100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', med=300)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        assert metric(adj_out[0]) == 300

        local_rib = g1.get_local_rib(q2)
        assert metric(local_rib[0]['paths'][0]) == 300

        adj_out = g1.get_adj_rib_out(q2)
        assert metric(adj_out[0]) == 200

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyMedSub").boot(env)
        lookup_scenario("ImportPolicyMedSub").setup(env)
        lookup_scenario("ImportPolicyMedSub").check(env)
        lookup_scenario("ImportPolicyMedSub").check2(env)


@register_scenario
class ExportPolicyMedReplace(object):
    """
    No.28 med replace action export-policy test
                     -------------------------------
    e1 ->(med=300)-> | -> q1-rib -> q1-adj-rib-out | ->(med=300)-> q1
                     |                             |
                     | -> q2-rib -> q2-adj-rib-out | ->(med=100)-> q2
                     |              apply action   |
                     -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action med set 100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', med=300)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        assert metric(adj_out[0]) == 300

        local_rib = g1.get_local_rib(q2)
        assert metric(local_rib[0]['paths'][0]) == 300

        adj_out = g1.get_adj_rib_out(q2)
        assert metric(adj_out[0]) == 100

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyMedReplace").boot(env)
        lookup_scenario("ExportPolicyMedReplace").setup(env)
        lookup_scenario("ExportPolicyMedReplace").check(env)
        lookup_scenario("ExportPolicyMedReplace").check2(env)


@register_scenario
class ExportPolicyMedAdd(object):
    """
    No.29 med add action export-policy test
                     -------------------------------
    e1 ->(med=300)-> | -> q1-rib -> q1-adj-rib-out | ->(med=300)->     q1
                     |                             |
                     | -> q2-rib -> q2-adj-rib-out | ->(med=300+100)-> q2
                     |              apply action   |
                     -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action med add 100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', med=300)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        assert metric(adj_out[0]) == 300

        local_rib = g1.get_local_rib(q2)
        assert metric(local_rib[0]['paths'][0]) == 300

        adj_out = g1.get_adj_rib_out(q2)
        assert metric(adj_out[0]) == 400

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyMedAdd").boot(env)
        lookup_scenario("ExportPolicyMedAdd").setup(env)
        lookup_scenario("ExportPolicyMedAdd").check(env)
        lookup_scenario("ExportPolicyMedAdd").check2(env)


@register_scenario
class ExportPolicyMedSub(object):
    """
    No.30 med subtract action export-policy test
                     -------------------------------
    e1 ->(med=300)-> | -> q1-rib -> q1-adj-rib-out | ->(med=300)->     q1
                     |                             |
                     | -> q2-rib -> q2-adj-rib-out | ->(med=300-100)-> q2
                     |              apply action   |
                     -------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy statement add st0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action med sub 100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.100.0/24', med=300)

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        q1 = env.q1
        q2 = env.q2

        adj_out = g1.get_adj_rib_out(q1)
        assert metric(adj_out[0]) == 300

        local_rib = g1.get_local_rib(q2)
        assert metric(local_rib[0]['paths'][0]) == 300

        adj_out = g1.get_adj_rib_out(q2)
        assert metric(adj_out[0]) == 200

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyMedSub").boot(env)
        lookup_scenario("ExportPolicyMedSub").setup(env)
        lookup_scenario("ExportPolicyMedSub").check(env)
        lookup_scenario("ExportPolicyMedSub").check2(env)


@register_scenario
class ExportPolicyAsPathPrepend(object):
    """
    No.37 aspath prepend action export
                            --------------------------------
    e1 ->(aspath=[65001])-> | ->  p1-rib -> p1-adj-rib-out | -> p1
                            |                              |
                            | ->  p2-rib -> p2-adj-rib-out | -> p2
                            |               apply action   |
                            --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.20.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action as-prepend 65005 5')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.20.0/24')
        e1.add_route('192.168.200.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        wait_for(lambda: len(g1.get_adj_rib_in(e1)) == 2)
        wait_for(lambda: len(g1.get_local_rib(q1)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q1)) == 2)
        wait_for(lambda: len(q1.get_global_rib()) == 2)
        wait_for(lambda: len(g1.get_local_rib(q2)) == 2)
        wait_for(lambda: len(g1.get_adj_rib_out(q2)) == 2)
        wait_for(lambda: len(q2.get_global_rib()) == 2)

    @staticmethod
    def check2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        path = g1.get_adj_rib_out(q1, prefix='192.168.20.0/24')[0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_adj_rib_out(q1, prefix='192.168.200.0/24')[0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_local_rib(q2, prefix='192.168.20.0/24')[0]['paths'][0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_adj_rib_out(q2, prefix='192.168.20.0/24')[0]
        assert path['aspath'] == ([65005] * 5 + [e1.asn])

        path = g1.get_adj_rib_out(q2, prefix='192.168.200.0/24')[0]
        assert path['aspath'] == [e1.asn]

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyAsPathPrepend").boot(env)
        lookup_scenario("ExportPolicyAsPathPrepend").setup(env)
        lookup_scenario("ExportPolicyAsPathPrepend").check(env)
        lookup_scenario("ExportPolicyAsPathPrepend").check2(env)


@register_scenario
class ImportPolicyAsPathPrependLastAS(object):
    """
    No.38 aspath prepend action lastas import
                            --------------------------------
    e1 ->(aspath=[65001])-> | ->  p1-rib -> p1-adj-rib-out | -> p1
                            |                              |
                            | ->  p2-rib -> p2-adj-rib-out | -> p2
                            |     apply action             |
                            --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.20.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action as-prepend last-as 5')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.20.0/24')
        e1.add_route('192.168.200.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ExportPolicyAsPathPrepend').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        path = g1.get_adj_rib_out(q1, prefix='192.168.20.0/24')[0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_adj_rib_out(q1, prefix='192.168.200.0/24')[0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_local_rib(q2, prefix='192.168.20.0/24')[0]['paths'][0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_adj_rib_out(q2, prefix='192.168.20.0/24')[0]
        assert path['aspath'] == ([e1.asn] * 5 + [e1.asn])

        path = g1.get_adj_rib_out(q2, prefix='192.168.200.0/24')[0]
        assert path['aspath'] == [e1.asn]

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyAsPathPrependLastAS").boot(env)
        lookup_scenario("ImportPolicyAsPathPrependLastAS").setup(env)
        lookup_scenario("ImportPolicyAsPathPrependLastAS").check(env)
        lookup_scenario("ImportPolicyAsPathPrependLastAS").check2(env)


@register_scenario
class ExportPolicyAsPathPrependLastAS(object):
    """
    No.39 aspath prepend action lastas export
                            --------------------------------
    e1 ->(aspath=[65001])-> | ->  p1-rib -> p1-adj-rib-out | -> p1
                            |                              |
                            | ->  p2-rib -> p2-adj-rib-out | -> p2
                            |     apply action             |
                            --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.20.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action as-prepend last-as 5')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.20.0/24')
        e1.add_route('192.168.200.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ExportPolicyAsPathPrepend').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        path = g1.get_adj_rib_out(q1, prefix='192.168.20.0/24')[0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_adj_rib_out(q1, prefix='192.168.200.0/24')[0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_local_rib(q2, prefix='192.168.20.0/24')[0]['paths'][0]
        assert path['aspath'] == [e1.asn]

        path = g1.get_adj_rib_out(q2, prefix='192.168.20.0/24')[0]
        assert path['aspath'] == ([e1.asn] * 5 + [e1.asn])

        path = g1.get_adj_rib_out(q2, prefix='192.168.200.0/24')[0]
        assert path['aspath'] == [e1.asn]

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyAsPathPrependLastAS").boot(env)
        lookup_scenario("ExportPolicyAsPathPrependLastAS").setup(env)
        lookup_scenario("ExportPolicyAsPathPrependLastAS").check(env)
        lookup_scenario("ExportPolicyAsPathPrependLastAS").check2(env)


@register_scenario
class ImportPolicyExCommunityOriginCondition(object):
    """
    No.40 extended community origin condition import
                                                 --------------------------------
    e1 ->(extcommunity=origin:65001.65100:200)-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                                 |                              |
                                                 | ->x q2-rib                   |
                                                 --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario("ImportPolicy").boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy ext-community add es0 soo:65001.65100:200')
        g1.local('gobgp policy statement st0 add condition ext-community es0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.20.0/24', extendedcommunity='origin:{0}:200'.format((65001 << 16) + 65100))
        e1.add_route('192.168.200.0/24', extendedcommunity='origin:{0}:100'.format((65001 << 16) + 65200))

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyExCommunityOriginCondition").boot(env)
        lookup_scenario("ImportPolicyExCommunityOriginCondition").setup(env)
        lookup_scenario("ImportPolicyExCommunityOriginCondition").check(env)


@register_scenario
class ImportPolicyExCommunityTargetCondition(object):
    """
    No.41 extended community origin condition import
                                           --------------------------------
    e1 ->(extcommunity=target:65010:320)-> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                           |                              |
                                           | ->x q2-rib                   |
                                           --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy ext-community add es0 rt:6[0-9]+:3[0-9]+')
        g1.local('gobgp policy statement st0 add condition ext-community es0')
        g1.local('gobgp policy statement st0 add action reject')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.20.0/24', extendedcommunity='target:65010:320')
        e1.add_route('192.168.200.0/24', extendedcommunity='target:55000:320')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario("ImportPolicy").check(env)

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyExCommunityTargetCondition").boot(env)
        lookup_scenario("ImportPolicyExCommunityTargetCondition").setup(env)
        lookup_scenario("ImportPolicyExCommunityTargetCondition").check(env)


def ext_community_exists(path, extcomm):
    typ = extcomm.split(':')[0]
    value = ':'.join(extcomm.split(':')[1:])
    for a in path['attrs']:
        if a['type'] == BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:
            for c in a['value']:
                if typ == 'RT' and c['type'] == 0 and c['subtype'] == 2 and c['value'] == value:
                    return True
    return False


@register_scenario
class ImportPolicyExCommunityAdd(object):
    """
    No.43 extended community add action import-policy test
                               ---------------------------------
    e1 ->(extcommunity=none) ->| ->  q1-rib ->  q1-adj-rib-out | --> q1
                               |                               |
                               | ->  q2-rib ->  q2-adj-rib-out | --> q2
                               |     add ext-community         |
                               ---------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.10.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action ext-community add rt:65000:1')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.10.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        path = g1.get_adj_rib_out(q1)[0]
        assert not ext_community_exists(path, 'RT:65000:1')
        path = g1.get_adj_rib_out(q2)[0]
        assert ext_community_exists(path, 'RT:65000:1')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyExCommunityAdd").boot(env)
        lookup_scenario("ImportPolicyExCommunityAdd").setup(env)
        lookup_scenario("ImportPolicyExCommunityAdd").check(env)
        lookup_scenario("ImportPolicyExCommunityAdd").check2(env)


@register_scenario
class ImportPolicyExCommunityAdd2(object):
    """
    No.44 extended community add action import-policy test
                                      --------------------------------
    e1 ->(extcommunity=RT:65000:1) -> | ->  q1-rib -> q1-adj-rib-out | --> q1
                                      |                              |
                                      | ->  q2-rib -> q2-adj-rib-out | --> q2
                                      |     add ext-community        |
                                      --------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.10.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action ext-community add rt:65100:100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.10.0/24', extendedcommunity='target:65000:1')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        path = g1.get_adj_rib_out(q1)[0]
        assert ext_community_exists(path, 'RT:65000:1')
        assert not ext_community_exists(path, 'RT:65100:100')
        path = g1.get_local_rib(q2)[0]['paths'][0]
        assert ext_community_exists(path, 'RT:65000:1')
        assert not ext_community_exists(path, 'RT:65100:100')
        path = g1.get_adj_rib_out(q2)[0]
        assert ext_community_exists(path, 'RT:65000:1')
        assert ext_community_exists(path, 'RT:65100:100')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyExCommunityAdd2").boot(env)
        lookup_scenario("ImportPolicyExCommunityAdd2").setup(env)
        lookup_scenario("ImportPolicyExCommunityAdd2").check(env)
        lookup_scenario("ImportPolicyExCommunityAdd2").check2(env)


@register_scenario
class ImportPolicyExCommunityMultipleAdd(object):
    """
    No.45 extended community add action multiple import-policy test
                                   ---------------------------------------
    exabgp ->(extcommunity=none) ->| ->  peer1-rib ->  peer1-adj-rib-out | --> peer1
                                   |                                     |
                                   | ->  peer2-rib ->  peer2-adj-rib-out | --> peer2
                                   |     add ext-community               |
                                   ---------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.10.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action ext-community add rt:65100:100 rt:100:100')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.10.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        path = g1.get_adj_rib_out(q1)[0]
        assert not ext_community_exists(path, 'RT:65100:100')
        assert not ext_community_exists(path, 'RT:100:100')
        path = g1.get_local_rib(q2)[0]['paths'][0]
        assert not ext_community_exists(path, 'RT:65100:100')
        assert not ext_community_exists(path, 'RT:100:100')
        path = g1.get_adj_rib_out(q2)[0]
        assert ext_community_exists(path, 'RT:65100:100')
        assert ext_community_exists(path, 'RT:100:100')

    @staticmethod
    def executor(env):
        lookup_scenario("ImportPolicyExCommunityMultipleAdd").boot(env)
        lookup_scenario("ImportPolicyExCommunityMultipleAdd").setup(env)
        lookup_scenario("ImportPolicyExCommunityMultipleAdd").check(env)
        lookup_scenario("ImportPolicyExCommunityMultipleAdd").check2(env)


@register_scenario
class ExportPolicyExCommunityAdd(object):
    """
    No.46 extended comunity add action export-policy test
                               ------------------------------------
    e1 ->(extcommunity=none) ->| ->  q1-rib ->  q1-adj-rib-out    | --> q1
                               |                                  |
                               | ->  q2-rib ->  q2-adj-rib-out    | --> q2
                               |                add ext-community |
                               ------------------------------------
    """
    @staticmethod
    def boot(env):
        lookup_scenario('ImportPolicy').boot(env)

    @staticmethod
    def setup(env):
        g1 = env.g1
        e1 = env.e1
        q1 = env.q1
        q2 = env.q2

        g1.local('gobgp policy prefix add ps0 192.168.10.0/24')
        g1.local('gobgp policy statement st0 add condition prefix ps0')
        g1.local('gobgp policy statement st0 add action accept')
        g1.local('gobgp policy statement st0 add action ext-community add rt:65000:1')
        g1.local('gobgp policy add policy0 st0')
        g1.local('gobgp neighbor {0} policy export add policy0'.format(g1.peers[q2]['neigh_addr'].split('/')[0]))

        e1.add_route('192.168.10.0/24')

        for c in [e1, q1, q2]:
            g1.wait_for(BGP_FSM_ESTABLISHED, c)

    @staticmethod
    def check(env):
        lookup_scenario('ImportPolicyCommunityAction').check(env)

    @staticmethod
    def check2(env):
        g1 = env.g1
        # e1 = env.e1
        q1 = env.q1
        q2 = env.q2
        path = g1.get_adj_rib_out(q1)[0]
        assert not ext_community_exists(path, 'RT:65000:1')
        path = g1.get_local_rib(q2)[0]['paths'][0]
        assert not ext_community_exists(path, 'RT:65000:1')
        path = g1.get_adj_rib_out(q2)[0]
        assert ext_community_exists(path, 'RT:65000:1')

    @staticmethod
    def executor(env):
        lookup_scenario("ExportPolicyExCommunityAdd").boot(env)
        lookup_scenario("ExportPolicyExCommunityAdd").setup(env)
        lookup_scenario("ExportPolicyExCommunityAdd").check(env)
        lookup_scenario("ExportPolicyExCommunityAdd").check2(env)


class TestGoBGPBase(unittest.TestCase):

    wait_per_retry = 5
    retry_limit = 10

    @classmethod
    def setUpClass(cls):
        idx = parser_option.test_index
        base.TEST_PREFIX = parser_option.test_prefix
        cls.parser_option = parser_option
        cls.executors = []
        if idx == 0:
            print('unset test-index. run all test sequential')
            for _, v in list(_SCENARIOS.items()):
                for k, m in inspect.getmembers(v, inspect.isfunction):
                    if k == 'executor':
                        cls.executor = m
                cls.executors.append(cls.executor)
        elif idx not in _SCENARIOS:
            print('invalid test-index. # of scenarios: {0}'.format(len(_SCENARIOS)))
            sys.exit(1)
        else:
            for k, m in inspect.getmembers(_SCENARIOS[idx], inspect.isfunction):
                if k == 'executor':
                    cls.executor = m
            cls.executors.append(cls.executor)

    def test(self):
        for e in self.executors:
            e(self)
            print('[PASS] %s' % e.__qualname__.split('.')[0], file=sys.stderr, flush=True)


