1from ryu.services.protocols.bgp.rtconf.base import ConfWithStats
2from ryu.services.protocols.bgp.rtconf.common import CommonConfListener
3from ryu.services.protocols.bgp.rtconf.neighbors import NeighborsConfListener
4from ryu.services.protocols.bgp.rtconf import vrfs
5from ryu.services.protocols.bgp.rtconf.vrfs import VrfConf
6from ryu.services.protocols.bgp.rtconf.vrfs import VrfsConfListener
7
8import logging
9
10LOG = logging.getLogger('bgpspeaker.core_managers.table_mixin')
11
12
13class ConfigurationManager(CommonConfListener, VrfsConfListener,
14                           NeighborsConfListener):
15    def __init__(self, core_service, common_conf, vrfs_conf, neighbors_conf):
16        self._signal_bus = core_service.signal_bus
17        self._common_config = common_conf
18        self._peer_manager = core_service.peer_manager
19        self._table_manager = core_service.table_manager
20        self._rt_manager = core_service.rt_manager
21        CommonConfListener.__init__(self, common_conf)
22        VrfsConfListener.__init__(self, vrfs_conf)
23        NeighborsConfListener.__init__(self, neighbors_conf)
24
25    def on_update_common_conf(self, evt):
26        raise NotImplementedError()
27
28    def on_add_neighbor_conf(self, evt):
29        neigh_conf = evt.value
30        self._peer_manager.add_peer(neigh_conf, self._common_config)
31
32    def on_remove_neighbor_conf(self, evt):
33        neigh_conf = evt.value
34        self._peer_manager.remove_peer(neigh_conf)
35
36    def on_chg_vrf_conf(self, evt):
37        evt_value = evt.value
38        vrf_conf = evt.src
39        new_imp_rts, removed_imp_rts, import_maps, re_export, re_import = \
40            evt_value
41        route_family = vrf_conf.route_family
42        vrf_table = self._table_manager.get_vrf_table(
43            vrf_conf.route_dist, route_family
44        )
45        assert vrf_table
46
47        # If we have new import RTs we have to update RTC table and make route
48        # refresh request to peers not participating in RT address-family
49        self._table_manager.update_vrf_table_links(
50            vrf_table, new_imp_rts, removed_imp_rts
51        )
52
53        # If other properties of VRF changed we re-install local paths.
54        if re_export:
55            self._table_manager.re_install_net_ctrl_paths(vrf_table)
56
57        # We have to withdraw paths that do not have any RT that are or
58        # interest
59        vrf_table.clean_uninteresting_paths()
60        if import_maps is not None:
61            vrf_table.init_import_maps(import_maps)
62            changed_dests = vrf_table.apply_import_maps()
63            for dest in changed_dests:
64                self._signal_bus.dest_changed(dest)
65
66        # import new rts
67        if re_import:
68            LOG.debug(
69                "RE-importing prefixes from VPN table to VRF %r", vrf_table
70            )
71            self._table_manager.import_all_vpn_paths_to_vrf(vrf_table)
72        else:
73            self._table_manager.import_all_vpn_paths_to_vrf(
74                vrf_table, new_imp_rts
75            )
76
77        # Update local/global RT NLRIs
78        self._rt_manager.update_local_rt_nlris()
79
80    def on_remove_vrf_conf(self, evt):
81        """Removes VRF table associated with given `vrf_conf`.
82
83        Cleans up other links to this table as well.
84        """
85        vrf_conf = evt.value
86        # Detach VrfConf change listener.
87        vrf_conf.remove_listener(VrfConf.VRF_CHG_EVT, self.on_chg_vrf_conf)
88
89        self._table_manager.remove_vrf_by_vrf_conf(vrf_conf)
90
91        # Update local RT NLRIs
92        self._rt_manager.update_local_rt_nlris()
93
94        self._signal_bus.vrf_removed(vrf_conf.route_dist)
95
96        # Remove AttributeMaps under the removed vrf
97        rd = vrf_conf.route_dist
98        rf = vrf_conf.route_family
99        peers = self._peer_manager.iterpeers
100        for peer in peers:
101            key = ':'.join([rd, rf])
102            peer.attribute_maps.pop(key, None)
103
104    def on_add_vrf_conf(self, evt):
105        """Event handler for new VrfConf.
106
107        Creates a VrfTable to store routing information related to new Vrf.
108        Also arranges for related paths to be imported to this VrfTable.
109        """
110        vrf_conf = evt.value
111        route_family = vrf_conf.route_family
112        assert route_family in vrfs.SUPPORTED_VRF_RF
113        # Create VRF table with given configuration.
114        vrf_table = self._table_manager.create_and_link_vrf_table(vrf_conf)
115
116        # Attach VrfConf change listeners.
117        vrf_conf.add_listener(ConfWithStats.UPDATE_STATS_LOG_ENABLED_EVT,
118                              self.on_stats_config_change)
119        vrf_conf.add_listener(ConfWithStats.UPDATE_STATS_TIME_EVT,
120                              self.on_stats_config_change)
121        vrf_conf.add_listener(VrfConf.VRF_CHG_EVT, self.on_chg_vrf_conf)
122
123        # Import paths from VPN table that match this VRF/VPN.
124        self._table_manager.import_all_vpn_paths_to_vrf(vrf_table)
125
126        # Update local RT NLRIs
127        self._rt_manager.update_local_rt_nlris()
128        self._signal_bus.vrf_added(vrf_conf)
129
130    def on_stats_config_change(self, evt):
131        vrf_conf = evt.src
132        self._signal_bus.stats_config_changed(vrf_conf)
133