backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / rtconf / vrfs.py
diff --git a/ryu/build/lib.linux-armv7l-2.7/ryu/services/protocols/bgp/rtconf/vrfs.py b/ryu/build/lib.linux-armv7l-2.7/ryu/services/protocols/bgp/rtconf/vrfs.py
new file mode 100644 (file)
index 0000000..72958df
--- /dev/null
@@ -0,0 +1,571 @@
+# Copyright (C) 2014 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.
+
+"""
+ Running or runtime configuration related to Virtual Routing and Forwarding
+ tables (VRFs).
+"""
+import abc
+import logging
+
+from ryu.lib.packet.bgp import RF_IPv4_UC
+from ryu.lib.packet.bgp import RF_IPv6_UC
+from ryu.lib.packet.bgp import RF_L2_EVPN
+from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC
+from ryu.lib.packet.bgp import RF_IPv6_FLOWSPEC
+from ryu.lib.packet.bgp import RF_L2VPN_FLOWSPEC
+
+from ryu.services.protocols.bgp.utils import validation
+from ryu.services.protocols.bgp.base import get_validator
+from ryu.services.protocols.bgp.rtconf.base import BaseConf
+from ryu.services.protocols.bgp.rtconf.base import BaseConfListener
+from ryu.services.protocols.bgp.rtconf.base import ConfigTypeError
+from ryu.services.protocols.bgp.rtconf.base import ConfigValueError
+from ryu.services.protocols.bgp.rtconf.base import ConfWithId
+from ryu.services.protocols.bgp.rtconf.base import ConfWithIdListener
+from ryu.services.protocols.bgp.rtconf.base import ConfWithStats
+from ryu.services.protocols.bgp.rtconf.base import ConfWithStatsListener
+from ryu.services.protocols.bgp.rtconf.base import MAX_NUM_EXPORT_RT
+from ryu.services.protocols.bgp.rtconf.base import MAX_NUM_IMPORT_RT
+from ryu.services.protocols.bgp.rtconf.base import MULTI_EXIT_DISC
+from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
+from ryu.services.protocols.bgp.rtconf.base import SITE_OF_ORIGINS
+from ryu.services.protocols.bgp.rtconf.base import validate
+from ryu.services.protocols.bgp.rtconf.base import validate_med
+from ryu.services.protocols.bgp.rtconf.base import validate_soo_list
+
+
+LOG = logging.getLogger('bgpspeaker.rtconf.vrfs')
+
+# Configuration setting names.
+ROUTE_DISTINGUISHER = 'route_dist'
+IMPORT_RTS = 'import_rts'
+EXPORT_RTS = 'export_rts'
+VRF_NAME = 'vrf_name'
+VRF_DESC = 'vrf_desc'
+VRF_RF = 'route_family'
+IMPORT_MAPS = 'import_maps'
+
+# Supported VRF route-families
+VRF_RF_IPV4 = 'ipv4'
+VRF_RF_IPV6 = 'ipv6'
+VRF_RF_L2_EVPN = 'evpn'
+VRF_RF_IPV4_FLOWSPEC = 'ipv4fs'
+VRF_RF_IPV6_FLOWSPEC = 'ipv6fs'
+VRF_RF_L2VPN_FLOWSPEC = 'l2vpnfs'
+SUPPORTED_VRF_RF = (
+    VRF_RF_IPV4,
+    VRF_RF_IPV6,
+    VRF_RF_L2_EVPN,
+    VRF_RF_IPV4_FLOWSPEC,
+    VRF_RF_IPV6_FLOWSPEC,
+    VRF_RF_L2VPN_FLOWSPEC,
+)
+
+
+# Default configuration values.
+DEFAULT_VRF_NAME = 'no-vrf-name'
+DEFAULT_VRF_DESC = 'no-vrf-desc'
+
+
+@validate(name=IMPORT_RTS)
+def validate_import_rts(import_rts):
+    if not isinstance(import_rts, list):
+        raise ConfigTypeError(conf_name=IMPORT_RTS, conf_value=import_rts)
+    if not (len(import_rts) <= MAX_NUM_IMPORT_RT):
+        raise ConfigValueError(desc='Max. import RT is limited to %s' %
+                               MAX_NUM_IMPORT_RT)
+    if not all(validation.is_valid_ext_comm_attr(rt) for rt in import_rts):
+        raise ConfigValueError(conf_name=IMPORT_RTS, conf_value=import_rts)
+    # Check if we have duplicates
+    unique_rts = set(import_rts)
+    if len(unique_rts) != len(import_rts):
+        raise ConfigValueError(desc='Duplicate value provided %s' % import_rts)
+
+    return import_rts
+
+
+@validate(name=EXPORT_RTS)
+def validate_export_rts(export_rts):
+    if not isinstance(export_rts, list):
+        raise ConfigTypeError(conf_name=EXPORT_RTS, conf_value=export_rts)
+    if not (len(export_rts) <= MAX_NUM_EXPORT_RT):
+        raise ConfigValueError(desc='Max. import RT is limited to %s' %
+                               MAX_NUM_EXPORT_RT)
+
+    if not all(validation.is_valid_ext_comm_attr(rt) for rt in export_rts):
+        raise ConfigValueError(conf_name=EXPORT_RTS, conf_value=export_rts)
+    # Check if we have duplicates
+    unique_rts = set(export_rts)
+    if len(unique_rts) != len(export_rts):
+        raise ConfigValueError(desc='Duplicate value provided in %s' %
+                               export_rts)
+    return export_rts
+
+
+@validate(name=ROUTE_DISTINGUISHER)
+def validate_rd(route_dist):
+    if not validation.is_valid_route_dist(route_dist):
+        raise ConfigValueError(conf_name=ROUTE_DISTINGUISHER,
+                               conf_value=route_dist)
+    return route_dist
+
+
+@validate(name=VRF_RF)
+def validate_vrf_rf(vrf_rf):
+    if vrf_rf not in SUPPORTED_VRF_RF:
+        raise ConfigValueError(desc='Give VRF route family %s is not '
+                               'supported.' % vrf_rf)
+    return vrf_rf
+
+
+class VrfConf(ConfWithId, ConfWithStats):
+    """Class that encapsulates configurations for one VRF."""
+
+    VRF_CHG_EVT = 'vrf_chg_evt'
+
+    VALID_EVT = frozenset([VRF_CHG_EVT])
+
+    REQUIRED_SETTINGS = frozenset([ROUTE_DISTINGUISHER,
+                                   IMPORT_RTS,
+                                   EXPORT_RTS])
+
+    OPTIONAL_SETTINGS = frozenset(
+        [VRF_NAME, MULTI_EXIT_DISC, SITE_OF_ORIGINS, VRF_RF, IMPORT_MAPS]
+    )
+
+    def __init__(self, **kwargs):
+        """Create an instance of VRF runtime configuration."""
+        super(VrfConf, self).__init__(**kwargs)
+
+    def _init_opt_settings(self, **kwargs):
+        super(VrfConf, self)._init_opt_settings(**kwargs)
+        # We do not have valid default MED value.
+        # If no MED attribute is provided then we do not have to use MED.
+        # If MED attribute is provided we have to validate it and use it.
+        med = kwargs.pop(MULTI_EXIT_DISC, None)
+        if med and validate_med(med):
+            self._settings[MULTI_EXIT_DISC] = med
+
+        # We do not have valid default SOO value.
+        # If no SOO attribute is provided then we do not have to use SOO.
+        # If SOO attribute is provided we have to validate it and use it.
+        soos = kwargs.pop(SITE_OF_ORIGINS, None)
+        if soos and validate_soo_list(soos):
+            self._settings[SITE_OF_ORIGINS] = soos
+
+        # Current we we only support VRF for IPv4 and IPv6 with default IPv4
+        vrf_rf = kwargs.pop(VRF_RF, VRF_RF_IPV4)
+        if vrf_rf and validate_vrf_rf(vrf_rf):
+            self._settings[VRF_RF] = vrf_rf
+
+        import_maps = kwargs.pop(IMPORT_MAPS, [])
+        self._settings[IMPORT_MAPS] = import_maps
+
+    # =========================================================================
+    # Required attributes
+    # =========================================================================
+
+    @property
+    def route_dist(self):
+        return self._settings[ROUTE_DISTINGUISHER]
+
+    # =========================================================================
+    # Optional attributes with valid defaults.
+    # =========================================================================
+
+    @property
+    def import_rts(self):
+        return list(self._settings[IMPORT_RTS])
+
+    @property
+    def export_rts(self):
+        return list(self._settings[EXPORT_RTS])
+
+    @property
+    def soo_list(self):
+        soos = self._settings.get(SITE_OF_ORIGINS)
+        if soos:
+            soos = list(soos)
+        else:
+            soos = []
+        return soos
+
+    @property
+    def multi_exit_disc(self):
+        """Returns configured value of MED, else None.
+
+        This configuration does not have default value.
+        """
+        return self._settings.get(MULTI_EXIT_DISC)
+
+    @property
+    def route_family(self):
+        """Returns configured route family for this VRF
+
+        This configuration does not change.
+        """
+        return self._settings.get(VRF_RF)
+
+    @property
+    def rd_rf_id(self):
+        return VrfConf.create_rd_rf_id(self.route_dist, self.route_family)
+
+    @property
+    def import_maps(self):
+        return self._settings.get(IMPORT_MAPS)
+
+    @staticmethod
+    def create_rd_rf_id(route_dist, route_family):
+        return route_dist, route_family
+
+    @staticmethod
+    def vrf_rf_2_rf(vrf_rf):
+        if vrf_rf == VRF_RF_IPV4:
+            return RF_IPv4_UC
+        elif vrf_rf == VRF_RF_IPV6:
+            return RF_IPv6_UC
+        elif vrf_rf == VRF_RF_L2_EVPN:
+            return RF_L2_EVPN
+        elif vrf_rf == VRF_RF_IPV4_FLOWSPEC:
+            return RF_IPv4_FLOWSPEC
+        elif vrf_rf == VRF_RF_IPV6_FLOWSPEC:
+            return RF_IPv6_FLOWSPEC
+        elif vrf_rf == VRF_RF_L2VPN_FLOWSPEC:
+            return RF_L2VPN_FLOWSPEC
+        else:
+            raise ValueError('Unsupported VRF route family given %s' % vrf_rf)
+
+    @staticmethod
+    def rf_2_vrf_rf(route_family):
+        if route_family == RF_IPv4_UC:
+            return VRF_RF_IPV4
+        elif route_family == RF_IPv6_UC:
+            return VRF_RF_IPV6
+        elif route_family == RF_L2_EVPN:
+            return VRF_RF_L2_EVPN
+        elif route_family == RF_IPv4_FLOWSPEC:
+            return VRF_RF_IPV4_FLOWSPEC
+        elif route_family == RF_IPv6_FLOWSPEC:
+            return VRF_RF_IPV6_FLOWSPEC
+        elif route_family == RF_L2VPN_FLOWSPEC:
+            return VRF_RF_L2VPN_FLOWSPEC
+        else:
+            raise ValueError('No supported mapping for route family '
+                             'to vrf_route_family exists for %s' %
+                             route_family)
+
+    @property
+    def settings(self):
+        """Returns a copy of current settings.
+
+        As some of the attributes are themselves containers, we clone the
+        settings to provide clones for those containers as well.
+        """
+        # Shallow copy first
+        cloned_setting = self._settings.copy()
+        # Don't want clone to link to same RT containers
+        cloned_setting[IMPORT_RTS] = self.import_rts
+        cloned_setting[EXPORT_RTS] = self.export_rts
+        cloned_setting[SITE_OF_ORIGINS] = self.soo_list
+        return cloned_setting
+
+    @classmethod
+    def get_opt_settings(cls):
+        self_confs = super(VrfConf, cls).get_opt_settings()
+        self_confs.update(VrfConf.OPTIONAL_SETTINGS)
+        return self_confs
+
+    @classmethod
+    def get_req_settings(cls):
+        self_confs = super(VrfConf, cls).get_req_settings()
+        self_confs.update(VrfConf.REQUIRED_SETTINGS)
+        return self_confs
+
+    @classmethod
+    def get_valid_evts(cls):
+        self_valid_evts = super(VrfConf, cls).get_valid_evts()
+        self_valid_evts.update(VrfConf.VALID_EVT)
+        return self_valid_evts
+
+    def update(self, **kwargs):
+        """Updates this `VrfConf` settings.
+
+        Notifies listeners if any settings changed. Returns `True` if update
+        was successful. This vrfs' route family, id and route dist settings
+        cannot be updated/changed.
+        """
+        # Update inherited configurations
+        super(VrfConf, self).update(**kwargs)
+        vrf_id = kwargs.get(ConfWithId.ID)
+        vrf_rd = kwargs.get(ROUTE_DISTINGUISHER)
+        vrf_rf = kwargs.get(VRF_RF)
+        if (vrf_id != self.id or
+                vrf_rd != self.route_dist or
+                vrf_rf != self.route_family):
+            raise ConfigValueError(desc='id/route-distinguisher/route-family'
+                                   ' do not match configured value.')
+
+        # Validate and update individual settings
+        new_imp_rts, old_imp_rts = \
+            self._update_import_rts(**kwargs)
+        export_rts_changed = self._update_export_rts(**kwargs)
+        soos_list_changed = self._update_soo_list(**kwargs)
+        med_changed = self._update_med(**kwargs)
+        re_export_needed = (export_rts_changed or
+                            soos_list_changed or
+                            med_changed)
+        import_maps = kwargs.get(IMPORT_MAPS, [])
+        re_import_needed = self._update_importmaps(import_maps)
+
+        # If we did have any change in value of any settings, we notify
+        # listeners
+        if (new_imp_rts is not None or
+                old_imp_rts is not None or
+                re_export_needed or re_import_needed):
+            evt_value = (
+                new_imp_rts,
+                old_imp_rts,
+                import_maps,
+                re_export_needed,
+                re_import_needed
+            )
+            self._notify_listeners(VrfConf.VRF_CHG_EVT, evt_value)
+        return True
+
+    def _update_import_rts(self, **kwargs):
+        import_rts = kwargs.get(IMPORT_RTS)
+        get_validator(IMPORT_RTS)(import_rts)
+        curr_import_rts = set(self._settings[IMPORT_RTS])
+
+        import_rts = set(import_rts)
+        if not import_rts.symmetric_difference(curr_import_rts):
+            return None, None
+
+        # Get the difference between current and new RTs
+        new_import_rts = import_rts - curr_import_rts
+        old_import_rts = curr_import_rts - import_rts
+
+        # Update current RTs and notify listeners.
+        self._settings[IMPORT_RTS] = import_rts
+        return new_import_rts, old_import_rts
+
+    def _update_export_rts(self, **kwargs):
+        export_rts = kwargs.get(EXPORT_RTS)
+        get_validator(EXPORT_RTS)(export_rts)
+        curr_export_rts = set(self._settings[EXPORT_RTS])
+
+        if curr_export_rts.symmetric_difference(export_rts):
+            # Update current RTs and notify listeners.
+            self._settings[EXPORT_RTS] = list(export_rts)
+            return True
+
+        return False
+
+    def _update_soo_list(self, **kwargs):
+        soo_list = kwargs.get(SITE_OF_ORIGINS, [])
+        get_validator(SITE_OF_ORIGINS)(soo_list)
+        curr_soos = set(self.soo_list)
+
+        # If given list is different from existing settings, we update it
+        if curr_soos.symmetric_difference(soo_list):
+            self._settings[SITE_OF_ORIGINS] = soo_list[:]
+            return True
+
+        return False
+
+    def _update_med(self, **kwargs):
+        multi_exit_disc = kwargs.get(MULTI_EXIT_DISC, None)
+        if multi_exit_disc:
+            get_validator(MULTI_EXIT_DISC)(multi_exit_disc)
+
+        if multi_exit_disc != self.multi_exit_disc:
+            self._settings[MULTI_EXIT_DISC] = multi_exit_disc
+            return True
+
+        return False
+
+    def _update_importmaps(self, import_maps):
+        if set(self._settings[IMPORT_MAPS]).symmetric_difference(import_maps):
+            self._settings[IMPORT_MAPS] = import_maps
+            return True
+
+        return False
+
+    def __repr__(self):
+        return ('<%s(route_dist: %r, import_rts: %r, export_rts: %r, '
+                'soo_list: %r)>' % (self.__class__.__name__,
+                                    self.route_dist, self.import_rts,
+                                    self.export_rts, self.soo_list))
+
+    def __str__(self):
+        return 'VrfConf-%s' % self.route_dist
+
+
+class VrfsConf(BaseConf):
+    """Container for all VRF configurations."""
+
+    ADD_VRF_CONF_EVT, REMOVE_VRF_CONF_EVT = range(2)
+
+    VALID_EVT = frozenset([ADD_VRF_CONF_EVT, REMOVE_VRF_CONF_EVT])
+
+    def __init__(self):
+        super(VrfsConf, self).__init__()
+        self._vrfs_by_rd_rf = {}
+        self._vrfs_by_id = {}
+
+    def _init_opt_settings(self, **kwargs):
+        pass
+
+    @property
+    def vrf_confs(self):
+        """Returns a list of configured `VrfConf`s
+        """
+        return list(self._vrfs_by_rd_rf.values())
+
+    @property
+    def vrf_interested_rts(self):
+        interested_rts = set()
+        for vrf_conf in self._vrfs_by_id.values():
+            interested_rts.update(vrf_conf.import_rts)
+        return interested_rts
+
+    def update(self, **kwargs):
+        raise NotImplementedError('Use either add/remove_vrf_conf'
+                                  ' methods instead.')
+
+    def add_vrf_conf(self, vrf_conf):
+        if vrf_conf.rd_rf_id in self._vrfs_by_rd_rf.keys():
+            raise RuntimeConfigError(
+                desc='VrfConf with rd_rf %s already exists'
+                     % str(vrf_conf.rd_rf_id)
+            )
+        if vrf_conf.id in self._vrfs_by_id:
+            raise RuntimeConfigError(
+                desc='VrfConf with id %s already exists' % str(vrf_conf.id)
+            )
+
+        self._vrfs_by_rd_rf[vrf_conf.rd_rf_id] = vrf_conf
+        self._vrfs_by_id[vrf_conf.id] = vrf_conf
+        self._notify_listeners(VrfsConf.ADD_VRF_CONF_EVT, vrf_conf)
+
+    def remove_vrf_conf(self, route_dist=None, vrf_id=None,
+                        vrf_rf=None):
+        """Removes any matching `VrfConf` for given `route_dist` or `vrf_id`
+
+        Parameters:
+            - `route_dist`: (str) route distinguisher of a configured VRF
+            - `vrf_id`: (str) vrf ID
+            - `vrf_rf`: (str) route family of the VRF configuration
+        If only `route_dist` is given, removes `VrfConf`s for all supported
+        address families for this `route_dist`. If `vrf_rf` is given, than only
+        removes `VrfConf` for that specific route family. If only `vrf_id` is
+        given, matching `VrfConf` will be removed.
+        """
+        if route_dist is None and vrf_id is None:
+            raise RuntimeConfigError(desc='To delete supply route_dist or id.')
+
+        # By default we remove all VRFs for given Id or RD
+        vrf_rfs = SUPPORTED_VRF_RF
+        # If asked to delete specific route family vrf conf.
+        if vrf_rf:
+            vrf_rfs = vrf_rf
+
+        # For all vrf route family asked to be deleted, we collect all deleted
+        # VrfConfs
+        removed_vrf_confs = []
+        for route_family in vrf_rfs:
+            if route_dist is not None:
+                rd_rf_id = VrfConf.create_rd_rf_id(route_dist, route_family)
+                vrf_conf = self._vrfs_by_rd_rf.pop(rd_rf_id, None)
+                if vrf_conf:
+                    self._vrfs_by_id.pop(vrf_conf.id, None)
+                    removed_vrf_confs.append(vrf_conf)
+            else:
+                vrf_conf = self._vrfs_by_id.pop(vrf_id, None)
+                if vrf_conf:
+                    self._vrfs_by_rd_rf.pop(vrf_conf.rd_rd_id, None)
+                    removed_vrf_confs.append(vrf_conf)
+
+        # We do not raise any exception if we cannot find asked VRF.
+        for vrf_conf in removed_vrf_confs:
+            self._notify_listeners(VrfsConf.REMOVE_VRF_CONF_EVT, vrf_conf)
+        return removed_vrf_confs
+
+    def get_vrf_conf(self, route_dist, vrf_rf, vrf_id=None):
+        if route_dist is None and vrf_id is None:
+            raise RuntimeConfigError(desc='To get VRF supply route_dist '
+                                     'or vrf_id.')
+        if route_dist is not None and vrf_id is not None:
+            vrf1 = self._vrfs_by_id.get(vrf_id)
+            rd_rf_id = VrfConf.create_rd_rf_id(route_dist, vrf_rf)
+            vrf2 = self._vrfs_by_rd_rf.get(rd_rf_id)
+            if vrf1 is not vrf2:
+                raise RuntimeConfigError(desc='Given VRF ID (%s) and RD (%s)'
+                                         ' are not of same VRF.' %
+                                         (vrf_id, route_dist))
+            vrf = vrf1
+        elif route_dist is not None:
+            rd_rf_id = VrfConf.create_rd_rf_id(route_dist, vrf_rf)
+            vrf = self._vrfs_by_rd_rf.get(rd_rf_id)
+        else:
+            vrf = self._vrfs_by_id.get(vrf_id)
+        return vrf
+
+    @property
+    def vrfs_by_rd_rf_id(self):
+        return dict(self._vrfs_by_rd_rf)
+
+    @classmethod
+    def get_valid_evts(cls):
+        self_valid_evts = super(VrfsConf, cls).get_valid_evts()
+        self_valid_evts.update(VrfsConf.VALID_EVT)
+        return self_valid_evts
+
+    def __repr__(self):
+        return '<%s(%r)>' % (self.__class__.__name__, self._vrfs_by_id)
+
+    @property
+    def settings(self):
+        return [vrf.settings for vrf in self._vrfs_by_id.values()]
+
+
+class VrfConfListener(ConfWithIdListener, ConfWithStatsListener):
+    """Base listener for various VRF configuration change event."""
+
+    def __init__(self, vrf_conf):
+        super(VrfConfListener, self).__init__(vrf_conf)
+        vrf_conf.add_listener(VrfConf.VRF_CHG_EVT, self.on_chg_vrf_conf)
+
+    def on_chg_vrf_conf(self, evt):
+        raise NotImplementedError('This method should be overridden')
+
+
+class VrfsConfListener(BaseConfListener):
+    """Base listener for VRF container change events."""
+
+    def __init__(self, vrfs_conf):
+        super(VrfsConfListener, self).__init__(vrfs_conf)
+        vrfs_conf.add_listener(VrfsConf.ADD_VRF_CONF_EVT, self.on_add_vrf_conf)
+        vrfs_conf.add_listener(VrfsConf.REMOVE_VRF_CONF_EVT,
+                               self.on_remove_vrf_conf)
+
+    @abc.abstractmethod
+    def on_add_vrf_conf(self, evt):
+        raise NotImplementedError('This method should be overridden')
+
+    @abc.abstractmethod
+    def on_remove_vrf_conf(self, evt):
+        raise NotImplementedError('This method should be overridden')