backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / rtconf / vrfs.py
1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17  Running or runtime configuration related to Virtual Routing and Forwarding
18  tables (VRFs).
19 """
20 import abc
21 import logging
22
23 from ryu.lib.packet.bgp import RF_IPv4_UC
24 from ryu.lib.packet.bgp import RF_IPv6_UC
25 from ryu.lib.packet.bgp import RF_L2_EVPN
26 from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC
27 from ryu.lib.packet.bgp import RF_IPv6_FLOWSPEC
28 from ryu.lib.packet.bgp import RF_L2VPN_FLOWSPEC
29
30 from ryu.services.protocols.bgp.utils import validation
31 from ryu.services.protocols.bgp.base import get_validator
32 from ryu.services.protocols.bgp.rtconf.base import BaseConf
33 from ryu.services.protocols.bgp.rtconf.base import BaseConfListener
34 from ryu.services.protocols.bgp.rtconf.base import ConfigTypeError
35 from ryu.services.protocols.bgp.rtconf.base import ConfigValueError
36 from ryu.services.protocols.bgp.rtconf.base import ConfWithId
37 from ryu.services.protocols.bgp.rtconf.base import ConfWithIdListener
38 from ryu.services.protocols.bgp.rtconf.base import ConfWithStats
39 from ryu.services.protocols.bgp.rtconf.base import ConfWithStatsListener
40 from ryu.services.protocols.bgp.rtconf.base import MAX_NUM_EXPORT_RT
41 from ryu.services.protocols.bgp.rtconf.base import MAX_NUM_IMPORT_RT
42 from ryu.services.protocols.bgp.rtconf.base import MULTI_EXIT_DISC
43 from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
44 from ryu.services.protocols.bgp.rtconf.base import SITE_OF_ORIGINS
45 from ryu.services.protocols.bgp.rtconf.base import validate
46 from ryu.services.protocols.bgp.rtconf.base import validate_med
47 from ryu.services.protocols.bgp.rtconf.base import validate_soo_list
48
49
50 LOG = logging.getLogger('bgpspeaker.rtconf.vrfs')
51
52 # Configuration setting names.
53 ROUTE_DISTINGUISHER = 'route_dist'
54 IMPORT_RTS = 'import_rts'
55 EXPORT_RTS = 'export_rts'
56 VRF_NAME = 'vrf_name'
57 VRF_DESC = 'vrf_desc'
58 VRF_RF = 'route_family'
59 IMPORT_MAPS = 'import_maps'
60
61 # Supported VRF route-families
62 VRF_RF_IPV4 = 'ipv4'
63 VRF_RF_IPV6 = 'ipv6'
64 VRF_RF_L2_EVPN = 'evpn'
65 VRF_RF_IPV4_FLOWSPEC = 'ipv4fs'
66 VRF_RF_IPV6_FLOWSPEC = 'ipv6fs'
67 VRF_RF_L2VPN_FLOWSPEC = 'l2vpnfs'
68 SUPPORTED_VRF_RF = (
69     VRF_RF_IPV4,
70     VRF_RF_IPV6,
71     VRF_RF_L2_EVPN,
72     VRF_RF_IPV4_FLOWSPEC,
73     VRF_RF_IPV6_FLOWSPEC,
74     VRF_RF_L2VPN_FLOWSPEC,
75 )
76
77
78 # Default configuration values.
79 DEFAULT_VRF_NAME = 'no-vrf-name'
80 DEFAULT_VRF_DESC = 'no-vrf-desc'
81
82
83 @validate(name=IMPORT_RTS)
84 def validate_import_rts(import_rts):
85     if not isinstance(import_rts, list):
86         raise ConfigTypeError(conf_name=IMPORT_RTS, conf_value=import_rts)
87     if not (len(import_rts) <= MAX_NUM_IMPORT_RT):
88         raise ConfigValueError(desc='Max. import RT is limited to %s' %
89                                MAX_NUM_IMPORT_RT)
90     if not all(validation.is_valid_ext_comm_attr(rt) for rt in import_rts):
91         raise ConfigValueError(conf_name=IMPORT_RTS, conf_value=import_rts)
92     # Check if we have duplicates
93     unique_rts = set(import_rts)
94     if len(unique_rts) != len(import_rts):
95         raise ConfigValueError(desc='Duplicate value provided %s' % import_rts)
96
97     return import_rts
98
99
100 @validate(name=EXPORT_RTS)
101 def validate_export_rts(export_rts):
102     if not isinstance(export_rts, list):
103         raise ConfigTypeError(conf_name=EXPORT_RTS, conf_value=export_rts)
104     if not (len(export_rts) <= MAX_NUM_EXPORT_RT):
105         raise ConfigValueError(desc='Max. import RT is limited to %s' %
106                                MAX_NUM_EXPORT_RT)
107
108     if not all(validation.is_valid_ext_comm_attr(rt) for rt in export_rts):
109         raise ConfigValueError(conf_name=EXPORT_RTS, conf_value=export_rts)
110     # Check if we have duplicates
111     unique_rts = set(export_rts)
112     if len(unique_rts) != len(export_rts):
113         raise ConfigValueError(desc='Duplicate value provided in %s' %
114                                export_rts)
115     return export_rts
116
117
118 @validate(name=ROUTE_DISTINGUISHER)
119 def validate_rd(route_dist):
120     if not validation.is_valid_route_dist(route_dist):
121         raise ConfigValueError(conf_name=ROUTE_DISTINGUISHER,
122                                conf_value=route_dist)
123     return route_dist
124
125
126 @validate(name=VRF_RF)
127 def validate_vrf_rf(vrf_rf):
128     if vrf_rf not in SUPPORTED_VRF_RF:
129         raise ConfigValueError(desc='Give VRF route family %s is not '
130                                'supported.' % vrf_rf)
131     return vrf_rf
132
133
134 class VrfConf(ConfWithId, ConfWithStats):
135     """Class that encapsulates configurations for one VRF."""
136
137     VRF_CHG_EVT = 'vrf_chg_evt'
138
139     VALID_EVT = frozenset([VRF_CHG_EVT])
140
141     REQUIRED_SETTINGS = frozenset([ROUTE_DISTINGUISHER,
142                                    IMPORT_RTS,
143                                    EXPORT_RTS])
144
145     OPTIONAL_SETTINGS = frozenset(
146         [VRF_NAME, MULTI_EXIT_DISC, SITE_OF_ORIGINS, VRF_RF, IMPORT_MAPS]
147     )
148
149     def __init__(self, **kwargs):
150         """Create an instance of VRF runtime configuration."""
151         super(VrfConf, self).__init__(**kwargs)
152
153     def _init_opt_settings(self, **kwargs):
154         super(VrfConf, self)._init_opt_settings(**kwargs)
155         # We do not have valid default MED value.
156         # If no MED attribute is provided then we do not have to use MED.
157         # If MED attribute is provided we have to validate it and use it.
158         med = kwargs.pop(MULTI_EXIT_DISC, None)
159         if med and validate_med(med):
160             self._settings[MULTI_EXIT_DISC] = med
161
162         # We do not have valid default SOO value.
163         # If no SOO attribute is provided then we do not have to use SOO.
164         # If SOO attribute is provided we have to validate it and use it.
165         soos = kwargs.pop(SITE_OF_ORIGINS, None)
166         if soos and validate_soo_list(soos):
167             self._settings[SITE_OF_ORIGINS] = soos
168
169         # Current we we only support VRF for IPv4 and IPv6 with default IPv4
170         vrf_rf = kwargs.pop(VRF_RF, VRF_RF_IPV4)
171         if vrf_rf and validate_vrf_rf(vrf_rf):
172             self._settings[VRF_RF] = vrf_rf
173
174         import_maps = kwargs.pop(IMPORT_MAPS, [])
175         self._settings[IMPORT_MAPS] = import_maps
176
177     # =========================================================================
178     # Required attributes
179     # =========================================================================
180
181     @property
182     def route_dist(self):
183         return self._settings[ROUTE_DISTINGUISHER]
184
185     # =========================================================================
186     # Optional attributes with valid defaults.
187     # =========================================================================
188
189     @property
190     def import_rts(self):
191         return list(self._settings[IMPORT_RTS])
192
193     @property
194     def export_rts(self):
195         return list(self._settings[EXPORT_RTS])
196
197     @property
198     def soo_list(self):
199         soos = self._settings.get(SITE_OF_ORIGINS)
200         if soos:
201             soos = list(soos)
202         else:
203             soos = []
204         return soos
205
206     @property
207     def multi_exit_disc(self):
208         """Returns configured value of MED, else None.
209
210         This configuration does not have default value.
211         """
212         return self._settings.get(MULTI_EXIT_DISC)
213
214     @property
215     def route_family(self):
216         """Returns configured route family for this VRF
217
218         This configuration does not change.
219         """
220         return self._settings.get(VRF_RF)
221
222     @property
223     def rd_rf_id(self):
224         return VrfConf.create_rd_rf_id(self.route_dist, self.route_family)
225
226     @property
227     def import_maps(self):
228         return self._settings.get(IMPORT_MAPS)
229
230     @staticmethod
231     def create_rd_rf_id(route_dist, route_family):
232         return route_dist, route_family
233
234     @staticmethod
235     def vrf_rf_2_rf(vrf_rf):
236         if vrf_rf == VRF_RF_IPV4:
237             return RF_IPv4_UC
238         elif vrf_rf == VRF_RF_IPV6:
239             return RF_IPv6_UC
240         elif vrf_rf == VRF_RF_L2_EVPN:
241             return RF_L2_EVPN
242         elif vrf_rf == VRF_RF_IPV4_FLOWSPEC:
243             return RF_IPv4_FLOWSPEC
244         elif vrf_rf == VRF_RF_IPV6_FLOWSPEC:
245             return RF_IPv6_FLOWSPEC
246         elif vrf_rf == VRF_RF_L2VPN_FLOWSPEC:
247             return RF_L2VPN_FLOWSPEC
248         else:
249             raise ValueError('Unsupported VRF route family given %s' % vrf_rf)
250
251     @staticmethod
252     def rf_2_vrf_rf(route_family):
253         if route_family == RF_IPv4_UC:
254             return VRF_RF_IPV4
255         elif route_family == RF_IPv6_UC:
256             return VRF_RF_IPV6
257         elif route_family == RF_L2_EVPN:
258             return VRF_RF_L2_EVPN
259         elif route_family == RF_IPv4_FLOWSPEC:
260             return VRF_RF_IPV4_FLOWSPEC
261         elif route_family == RF_IPv6_FLOWSPEC:
262             return VRF_RF_IPV6_FLOWSPEC
263         elif route_family == RF_L2VPN_FLOWSPEC:
264             return VRF_RF_L2VPN_FLOWSPEC
265         else:
266             raise ValueError('No supported mapping for route family '
267                              'to vrf_route_family exists for %s' %
268                              route_family)
269
270     @property
271     def settings(self):
272         """Returns a copy of current settings.
273
274         As some of the attributes are themselves containers, we clone the
275         settings to provide clones for those containers as well.
276         """
277         # Shallow copy first
278         cloned_setting = self._settings.copy()
279         # Don't want clone to link to same RT containers
280         cloned_setting[IMPORT_RTS] = self.import_rts
281         cloned_setting[EXPORT_RTS] = self.export_rts
282         cloned_setting[SITE_OF_ORIGINS] = self.soo_list
283         return cloned_setting
284
285     @classmethod
286     def get_opt_settings(cls):
287         self_confs = super(VrfConf, cls).get_opt_settings()
288         self_confs.update(VrfConf.OPTIONAL_SETTINGS)
289         return self_confs
290
291     @classmethod
292     def get_req_settings(cls):
293         self_confs = super(VrfConf, cls).get_req_settings()
294         self_confs.update(VrfConf.REQUIRED_SETTINGS)
295         return self_confs
296
297     @classmethod
298     def get_valid_evts(cls):
299         self_valid_evts = super(VrfConf, cls).get_valid_evts()
300         self_valid_evts.update(VrfConf.VALID_EVT)
301         return self_valid_evts
302
303     def update(self, **kwargs):
304         """Updates this `VrfConf` settings.
305
306         Notifies listeners if any settings changed. Returns `True` if update
307         was successful. This vrfs' route family, id and route dist settings
308         cannot be updated/changed.
309         """
310         # Update inherited configurations
311         super(VrfConf, self).update(**kwargs)
312         vrf_id = kwargs.get(ConfWithId.ID)
313         vrf_rd = kwargs.get(ROUTE_DISTINGUISHER)
314         vrf_rf = kwargs.get(VRF_RF)
315         if (vrf_id != self.id or
316                 vrf_rd != self.route_dist or
317                 vrf_rf != self.route_family):
318             raise ConfigValueError(desc='id/route-distinguisher/route-family'
319                                    ' do not match configured value.')
320
321         # Validate and update individual settings
322         new_imp_rts, old_imp_rts = \
323             self._update_import_rts(**kwargs)
324         export_rts_changed = self._update_export_rts(**kwargs)
325         soos_list_changed = self._update_soo_list(**kwargs)
326         med_changed = self._update_med(**kwargs)
327         re_export_needed = (export_rts_changed or
328                             soos_list_changed or
329                             med_changed)
330         import_maps = kwargs.get(IMPORT_MAPS, [])
331         re_import_needed = self._update_importmaps(import_maps)
332
333         # If we did have any change in value of any settings, we notify
334         # listeners
335         if (new_imp_rts is not None or
336                 old_imp_rts is not None or
337                 re_export_needed or re_import_needed):
338             evt_value = (
339                 new_imp_rts,
340                 old_imp_rts,
341                 import_maps,
342                 re_export_needed,
343                 re_import_needed
344             )
345             self._notify_listeners(VrfConf.VRF_CHG_EVT, evt_value)
346         return True
347
348     def _update_import_rts(self, **kwargs):
349         import_rts = kwargs.get(IMPORT_RTS)
350         get_validator(IMPORT_RTS)(import_rts)
351         curr_import_rts = set(self._settings[IMPORT_RTS])
352
353         import_rts = set(import_rts)
354         if not import_rts.symmetric_difference(curr_import_rts):
355             return None, None
356
357         # Get the difference between current and new RTs
358         new_import_rts = import_rts - curr_import_rts
359         old_import_rts = curr_import_rts - import_rts
360
361         # Update current RTs and notify listeners.
362         self._settings[IMPORT_RTS] = import_rts
363         return new_import_rts, old_import_rts
364
365     def _update_export_rts(self, **kwargs):
366         export_rts = kwargs.get(EXPORT_RTS)
367         get_validator(EXPORT_RTS)(export_rts)
368         curr_export_rts = set(self._settings[EXPORT_RTS])
369
370         if curr_export_rts.symmetric_difference(export_rts):
371             # Update current RTs and notify listeners.
372             self._settings[EXPORT_RTS] = list(export_rts)
373             return True
374
375         return False
376
377     def _update_soo_list(self, **kwargs):
378         soo_list = kwargs.get(SITE_OF_ORIGINS, [])
379         get_validator(SITE_OF_ORIGINS)(soo_list)
380         curr_soos = set(self.soo_list)
381
382         # If given list is different from existing settings, we update it
383         if curr_soos.symmetric_difference(soo_list):
384             self._settings[SITE_OF_ORIGINS] = soo_list[:]
385             return True
386
387         return False
388
389     def _update_med(self, **kwargs):
390         multi_exit_disc = kwargs.get(MULTI_EXIT_DISC, None)
391         if multi_exit_disc:
392             get_validator(MULTI_EXIT_DISC)(multi_exit_disc)
393
394         if multi_exit_disc != self.multi_exit_disc:
395             self._settings[MULTI_EXIT_DISC] = multi_exit_disc
396             return True
397
398         return False
399
400     def _update_importmaps(self, import_maps):
401         if set(self._settings[IMPORT_MAPS]).symmetric_difference(import_maps):
402             self._settings[IMPORT_MAPS] = import_maps
403             return True
404
405         return False
406
407     def __repr__(self):
408         return ('<%s(route_dist: %r, import_rts: %r, export_rts: %r, '
409                 'soo_list: %r)>' % (self.__class__.__name__,
410                                     self.route_dist, self.import_rts,
411                                     self.export_rts, self.soo_list))
412
413     def __str__(self):
414         return 'VrfConf-%s' % self.route_dist
415
416
417 class VrfsConf(BaseConf):
418     """Container for all VRF configurations."""
419
420     ADD_VRF_CONF_EVT, REMOVE_VRF_CONF_EVT = range(2)
421
422     VALID_EVT = frozenset([ADD_VRF_CONF_EVT, REMOVE_VRF_CONF_EVT])
423
424     def __init__(self):
425         super(VrfsConf, self).__init__()
426         self._vrfs_by_rd_rf = {}
427         self._vrfs_by_id = {}
428
429     def _init_opt_settings(self, **kwargs):
430         pass
431
432     @property
433     def vrf_confs(self):
434         """Returns a list of configured `VrfConf`s
435         """
436         return list(self._vrfs_by_rd_rf.values())
437
438     @property
439     def vrf_interested_rts(self):
440         interested_rts = set()
441         for vrf_conf in self._vrfs_by_id.values():
442             interested_rts.update(vrf_conf.import_rts)
443         return interested_rts
444
445     def update(self, **kwargs):
446         raise NotImplementedError('Use either add/remove_vrf_conf'
447                                   ' methods instead.')
448
449     def add_vrf_conf(self, vrf_conf):
450         if vrf_conf.rd_rf_id in self._vrfs_by_rd_rf.keys():
451             raise RuntimeConfigError(
452                 desc='VrfConf with rd_rf %s already exists'
453                      % str(vrf_conf.rd_rf_id)
454             )
455         if vrf_conf.id in self._vrfs_by_id:
456             raise RuntimeConfigError(
457                 desc='VrfConf with id %s already exists' % str(vrf_conf.id)
458             )
459
460         self._vrfs_by_rd_rf[vrf_conf.rd_rf_id] = vrf_conf
461         self._vrfs_by_id[vrf_conf.id] = vrf_conf
462         self._notify_listeners(VrfsConf.ADD_VRF_CONF_EVT, vrf_conf)
463
464     def remove_vrf_conf(self, route_dist=None, vrf_id=None,
465                         vrf_rf=None):
466         """Removes any matching `VrfConf` for given `route_dist` or `vrf_id`
467
468         Parameters:
469             - `route_dist`: (str) route distinguisher of a configured VRF
470             - `vrf_id`: (str) vrf ID
471             - `vrf_rf`: (str) route family of the VRF configuration
472         If only `route_dist` is given, removes `VrfConf`s for all supported
473         address families for this `route_dist`. If `vrf_rf` is given, than only
474         removes `VrfConf` for that specific route family. If only `vrf_id` is
475         given, matching `VrfConf` will be removed.
476         """
477         if route_dist is None and vrf_id is None:
478             raise RuntimeConfigError(desc='To delete supply route_dist or id.')
479
480         # By default we remove all VRFs for given Id or RD
481         vrf_rfs = SUPPORTED_VRF_RF
482         # If asked to delete specific route family vrf conf.
483         if vrf_rf:
484             vrf_rfs = vrf_rf
485
486         # For all vrf route family asked to be deleted, we collect all deleted
487         # VrfConfs
488         removed_vrf_confs = []
489         for route_family in vrf_rfs:
490             if route_dist is not None:
491                 rd_rf_id = VrfConf.create_rd_rf_id(route_dist, route_family)
492                 vrf_conf = self._vrfs_by_rd_rf.pop(rd_rf_id, None)
493                 if vrf_conf:
494                     self._vrfs_by_id.pop(vrf_conf.id, None)
495                     removed_vrf_confs.append(vrf_conf)
496             else:
497                 vrf_conf = self._vrfs_by_id.pop(vrf_id, None)
498                 if vrf_conf:
499                     self._vrfs_by_rd_rf.pop(vrf_conf.rd_rd_id, None)
500                     removed_vrf_confs.append(vrf_conf)
501
502         # We do not raise any exception if we cannot find asked VRF.
503         for vrf_conf in removed_vrf_confs:
504             self._notify_listeners(VrfsConf.REMOVE_VRF_CONF_EVT, vrf_conf)
505         return removed_vrf_confs
506
507     def get_vrf_conf(self, route_dist, vrf_rf, vrf_id=None):
508         if route_dist is None and vrf_id is None:
509             raise RuntimeConfigError(desc='To get VRF supply route_dist '
510                                      'or vrf_id.')
511         if route_dist is not None and vrf_id is not None:
512             vrf1 = self._vrfs_by_id.get(vrf_id)
513             rd_rf_id = VrfConf.create_rd_rf_id(route_dist, vrf_rf)
514             vrf2 = self._vrfs_by_rd_rf.get(rd_rf_id)
515             if vrf1 is not vrf2:
516                 raise RuntimeConfigError(desc='Given VRF ID (%s) and RD (%s)'
517                                          ' are not of same VRF.' %
518                                          (vrf_id, route_dist))
519             vrf = vrf1
520         elif route_dist is not None:
521             rd_rf_id = VrfConf.create_rd_rf_id(route_dist, vrf_rf)
522             vrf = self._vrfs_by_rd_rf.get(rd_rf_id)
523         else:
524             vrf = self._vrfs_by_id.get(vrf_id)
525         return vrf
526
527     @property
528     def vrfs_by_rd_rf_id(self):
529         return dict(self._vrfs_by_rd_rf)
530
531     @classmethod
532     def get_valid_evts(cls):
533         self_valid_evts = super(VrfsConf, cls).get_valid_evts()
534         self_valid_evts.update(VrfsConf.VALID_EVT)
535         return self_valid_evts
536
537     def __repr__(self):
538         return '<%s(%r)>' % (self.__class__.__name__, self._vrfs_by_id)
539
540     @property
541     def settings(self):
542         return [vrf.settings for vrf in self._vrfs_by_id.values()]
543
544
545 class VrfConfListener(ConfWithIdListener, ConfWithStatsListener):
546     """Base listener for various VRF configuration change event."""
547
548     def __init__(self, vrf_conf):
549         super(VrfConfListener, self).__init__(vrf_conf)
550         vrf_conf.add_listener(VrfConf.VRF_CHG_EVT, self.on_chg_vrf_conf)
551
552     def on_chg_vrf_conf(self, evt):
553         raise NotImplementedError('This method should be overridden')
554
555
556 class VrfsConfListener(BaseConfListener):
557     """Base listener for VRF container change events."""
558
559     def __init__(self, vrfs_conf):
560         super(VrfsConfListener, self).__init__(vrfs_conf)
561         vrfs_conf.add_listener(VrfsConf.ADD_VRF_CONF_EVT, self.on_add_vrf_conf)
562         vrfs_conf.add_listener(VrfsConf.REMOVE_VRF_CONF_EVT,
563                                self.on_remove_vrf_conf)
564
565     @abc.abstractmethod
566     def on_add_vrf_conf(self, evt):
567         raise NotImplementedError('This method should be overridden')
568
569     @abc.abstractmethod
570     def on_remove_vrf_conf(self, evt):
571         raise NotImplementedError('This method should be overridden')