backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / rtconf / base.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 base classes.
18 """
19 from abc import ABCMeta
20 from abc import abstractmethod
21 import functools
22 import numbers
23 import logging
24 import uuid
25
26 import six
27
28 from ryu.services.protocols.bgp.base import add_bgp_error_metadata
29 from ryu.services.protocols.bgp.base import BGPSException
30 from ryu.services.protocols.bgp.base import get_validator
31 from ryu.services.protocols.bgp.base import RUNTIME_CONF_ERROR_CODE
32 from ryu.services.protocols.bgp.base import validate
33 from ryu.services.protocols.bgp.utils import validation
34 from ryu.services.protocols.bgp.utils.validation import is_valid_asn
35
36 LOG = logging.getLogger('bgpspeaker.rtconf.base')
37
38 #
39 # Nested settings.
40 #
41 CAP_REFRESH = 'cap_refresh'
42 CAP_ENHANCED_REFRESH = 'cap_enhanced_refresh'
43 CAP_FOUR_OCTET_AS_NUMBER = 'cap_four_octet_as_number'
44 CAP_MBGP_IPV4 = 'cap_mbgp_ipv4'
45 CAP_MBGP_IPV6 = 'cap_mbgp_ipv6'
46 CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4'
47 CAP_MBGP_VPNV6 = 'cap_mbgp_vpnv6'
48 CAP_MBGP_EVPN = 'cap_mbgp_evpn'
49 CAP_MBGP_IPV4FS = 'cap_mbgp_ipv4fs'
50 CAP_MBGP_IPV6FS = 'cap_mbgp_ipv6fs'
51 CAP_MBGP_VPNV4FS = 'cap_mbgp_vpnv4fs'
52 CAP_MBGP_VPNV6FS = 'cap_mbgp_vpnv6fs'
53 CAP_MBGP_L2VPNFS = 'cap_mbgp_l2vpnfs'
54 CAP_RTC = 'cap_rtc'
55 RTC_AS = 'rtc_as'
56 HOLD_TIME = 'hold_time'
57
58 # To control how many prefixes can be received from a neighbor.
59 # 0 value indicates no limit and other related options will be ignored.
60 # Current behavior is to log that limit has reached.
61 MAX_PREFIXES = 'max_prefixes'
62
63 # Has same meaning as: http://www.juniper.net/techpubs/software/junos/junos94
64 # /swconfig-routing/disabling-suppression-of-route-
65 # advertisements.html#id-13255463
66 ADVERTISE_PEER_AS = 'advertise_peer_as'
67
68 # MED - MULTI_EXIT_DISC
69 MULTI_EXIT_DISC = 'multi_exit_disc'
70
71 # Extended community attribute route origin.
72 SITE_OF_ORIGINS = 'site_of_origins'
73
74 # Constants related to errors.
75 CONF_NAME = 'conf_name'
76 CONF_VALUE = 'conf_value'
77
78 # Max. value  limits
79 MAX_NUM_IMPORT_RT = 1000
80 MAX_NUM_EXPORT_RT = 250
81 MAX_NUM_SOO = 10
82
83
84 # =============================================================================
85 # Runtime configuration errors or exceptions.
86 # =============================================================================
87
88 @add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=1,
89                         def_desc='Error with runtime-configuration.')
90 class RuntimeConfigError(BGPSException):
91     """Base class for all runtime configuration errors.
92     """
93     pass
94
95
96 @add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=2,
97                         def_desc='Missing required configuration.')
98 class MissingRequiredConf(RuntimeConfigError):
99     """Exception raised when trying to configure with missing required
100     settings.
101     """
102
103     def __init__(self, **kwargs):
104         conf_name = kwargs.get('conf_name')
105         if conf_name:
106             super(MissingRequiredConf, self).__init__(
107                 desc='Missing required configuration: %s' % conf_name)
108         else:
109             super(MissingRequiredConf, self).__init__(desc=kwargs.get('desc'))
110
111
112 @add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=3,
113                         def_desc='Incorrect Type for configuration.')
114 class ConfigTypeError(RuntimeConfigError):
115     """Exception raised when configuration value type miss-match happens.
116     """
117
118     def __init__(self, **kwargs):
119         conf_name = kwargs.get(CONF_NAME)
120         conf_value = kwargs.get(CONF_VALUE)
121         if conf_name and conf_value:
122             super(ConfigTypeError, self).__init__(
123                 desc='Incorrect Type %s for configuration: %s' %
124                 (conf_value, conf_name))
125         elif conf_name:
126             super(ConfigTypeError, self).__init__(
127                 desc='Incorrect Type for configuration: %s' % conf_name)
128         else:
129             super(ConfigTypeError, self).__init__(desc=kwargs.get('desc'))
130
131
132 @add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=4,
133                         def_desc='Incorrect Value for configuration.')
134 class ConfigValueError(RuntimeConfigError):
135     """Exception raised when configuration value is of correct type but
136     incorrect value.
137     """
138
139     def __init__(self, **kwargs):
140         conf_name = kwargs.get(CONF_NAME)
141         conf_value = kwargs.get(CONF_VALUE)
142         if conf_name and conf_value:
143             super(ConfigValueError, self).__init__(
144                 desc='Incorrect Value %s for configuration: %s' %
145                 (conf_value, conf_name))
146         elif conf_name:
147             super(ConfigValueError, self).__init__(
148                 desc='Incorrect Value for configuration: %s' % conf_name)
149         else:
150             super(ConfigValueError, self).__init__(desc=kwargs.get('desc'))
151
152
153 # =============================================================================
154 # Configuration base classes.
155 # =============================================================================
156
157 @six.add_metaclass(ABCMeta)
158 class BaseConf(object):
159     """Base class for a set of configuration values.
160
161     Configurations can be required or optional. Also acts as a container of
162     configuration change listeners.
163     """
164
165     def __init__(self, **kwargs):
166         self._req_settings = self.get_req_settings()
167         self._opt_settings = self.get_opt_settings()
168         self._valid_evts = self.get_valid_evts()
169         self._listeners = {}
170         self._settings = {}
171
172         # validate required and unknown settings
173         self._validate_req_unknown_settings(**kwargs)
174
175         # Initialize configuration settings.
176         self._init_req_settings(**kwargs)
177         self._init_opt_settings(**kwargs)
178
179     @property
180     def settings(self):
181         """Returns a copy of current settings."""
182         return self._settings.copy()
183
184     @classmethod
185     def get_valid_evts(cls):
186         return set()
187
188     @classmethod
189     def get_req_settings(cls):
190         return set()
191
192     @classmethod
193     def get_opt_settings(cls):
194         return set()
195
196     @abstractmethod
197     def _init_opt_settings(self, **kwargs):
198         """Sub-classes should override this method to initialize optional
199          settings.
200         """
201         pass
202
203     @abstractmethod
204     def update(self, **kwargs):
205         # Validate given values
206         self._validate_req_unknown_settings(**kwargs)
207
208     def _validate_req_unknown_settings(self, **kwargs):
209         """Checks if required settings are present.
210
211         Also checks if unknown requirements are present.
212         """
213         # Validate given configuration.
214         self._all_attrs = (self._req_settings | self._opt_settings)
215         if not kwargs and len(self._req_settings) > 0:
216             raise MissingRequiredConf(desc='Missing all required attributes.')
217
218         given_attrs = frozenset(kwargs.keys())
219         unknown_attrs = given_attrs - self._all_attrs
220         if unknown_attrs:
221             raise RuntimeConfigError(desc=(
222                 'Unknown attributes: %s' %
223                 ', '.join([str(i) for i in unknown_attrs])))
224         missing_req_settings = self._req_settings - given_attrs
225         if missing_req_settings:
226             raise MissingRequiredConf(conf_name=list(missing_req_settings))
227
228     def _init_req_settings(self, **kwargs):
229         for req_attr in self._req_settings:
230             req_attr_value = kwargs.get(req_attr)
231             if req_attr_value is None:
232                 raise MissingRequiredConf(conf_name=req_attr_value)
233             # Validate attribute value
234             req_attr_value = get_validator(req_attr)(req_attr_value)
235             self._settings[req_attr] = req_attr_value
236
237     def add_listener(self, evt, callback):
238         #   if (evt not in self.get_valid_evts()):
239         #       raise RuntimeConfigError(desc=('Unknown event %s' % evt))
240
241         listeners = self._listeners.get(evt, None)
242         if not listeners:
243             listeners = set()
244             self._listeners[evt] = listeners
245         listeners.update([callback])
246
247     def remove_listener(self, evt, callback):
248         if evt in self.get_valid_evts():
249             listeners = self._listeners.get(evt, None)
250             if listeners and (callback in listeners):
251                 listeners.remove(callback)
252                 return True
253
254         return False
255
256     def _notify_listeners(self, evt, value):
257         listeners = self._listeners.get(evt, [])
258         for callback in listeners:
259             callback(ConfEvent(self, evt, value))
260
261     def __repr__(self):
262         return '%s(%r)' % (self.__class__, self._settings)
263
264
265 class ConfWithId(BaseConf):
266     """Configuration settings related to identity."""
267     # Config./resource identifier.
268     ID = 'id'
269     # Config./resource name.
270     NAME = 'name'
271     # Config./resource description.
272     DESCRIPTION = 'description'
273
274     UPDATE_NAME_EVT = 'update_name_evt'
275     UPDATE_DESCRIPTION_EVT = 'update_description_evt'
276
277     VALID_EVT = frozenset([UPDATE_NAME_EVT, UPDATE_DESCRIPTION_EVT])
278     OPTIONAL_SETTINGS = frozenset([ID, NAME, DESCRIPTION])
279
280     def __init__(self, **kwargs):
281         super(ConfWithId, self).__init__(**kwargs)
282
283     @classmethod
284     def get_opt_settings(cls):
285         self_confs = super(ConfWithId, cls).get_opt_settings()
286         self_confs.update(ConfWithId.OPTIONAL_SETTINGS)
287         return self_confs
288
289     @classmethod
290     def get_req_settings(cls):
291         self_confs = super(ConfWithId, cls).get_req_settings()
292         return self_confs
293
294     @classmethod
295     def get_valid_evts(cls):
296         self_valid_evts = super(ConfWithId, cls).get_valid_evts()
297         self_valid_evts.update(ConfWithId.VALID_EVT)
298         return self_valid_evts
299
300     def _init_opt_settings(self, **kwargs):
301         super(ConfWithId, self)._init_opt_settings(**kwargs)
302         self._settings[ConfWithId.ID] = \
303             compute_optional_conf(ConfWithId.ID, str(uuid.uuid4()), **kwargs)
304         self._settings[ConfWithId.NAME] = \
305             compute_optional_conf(ConfWithId.NAME, str(self), **kwargs)
306         self._settings[ConfWithId.DESCRIPTION] = \
307             compute_optional_conf(ConfWithId.DESCRIPTION, str(self), **kwargs)
308
309     @property
310     def id(self):
311         return self._settings[ConfWithId.ID]
312
313     @property
314     def name(self):
315         return self._settings[ConfWithId.NAME]
316
317     @name.setter
318     def name(self, new_name):
319         old_name = self.name
320         if not new_name:
321             new_name = repr(self)
322         else:
323             get_validator(ConfWithId.NAME)(new_name)
324
325         if old_name != new_name:
326             self._settings[ConfWithId.NAME] = new_name
327             self._notify_listeners(ConfWithId.UPDATE_NAME_EVT,
328                                    (old_name, self.name))
329
330     @property
331     def description(self):
332         return self._settings[ConfWithId.DESCRIPTION]
333
334     @description.setter
335     def description(self, new_description):
336         old_desc = self.description
337         if not new_description:
338             new_description = str(self)
339         else:
340             get_validator(ConfWithId.DESCRIPTION)(new_description)
341
342         if old_desc != new_description:
343             self._settings[ConfWithId.DESCRIPTION] = new_description
344             self._notify_listeners(ConfWithId.UPDATE_DESCRIPTION_EVT,
345                                    (old_desc, self.description))
346
347     def update(self, **kwargs):
348         # Update inherited configurations
349         super(ConfWithId, self).update(**kwargs)
350         self.name = compute_optional_conf(ConfWithId.NAME,
351                                           str(self),
352                                           **kwargs)
353         self.description = compute_optional_conf(ConfWithId.DESCRIPTION,
354                                                  str(self),
355                                                  **kwargs)
356
357
358 class ConfWithStats(BaseConf):
359     """Configuration settings related to statistics collection."""
360
361     # Enable or disable statistics logging.
362     STATS_LOG_ENABLED = 'statistics_log_enabled'
363     DEFAULT_STATS_LOG_ENABLED = False
364
365     # Statistics logging time.
366     STATS_TIME = 'statistics_interval'
367     DEFAULT_STATS_TIME = 60
368
369     UPDATE_STATS_LOG_ENABLED_EVT = 'update_stats_log_enabled_evt'
370     UPDATE_STATS_TIME_EVT = 'update_stats_time_evt'
371
372     VALID_EVT = frozenset([UPDATE_STATS_LOG_ENABLED_EVT,
373                            UPDATE_STATS_TIME_EVT])
374     OPTIONAL_SETTINGS = frozenset([STATS_LOG_ENABLED, STATS_TIME])
375
376     def __init__(self, **kwargs):
377         super(ConfWithStats, self).__init__(**kwargs)
378
379     def _init_opt_settings(self, **kwargs):
380         super(ConfWithStats, self)._init_opt_settings(**kwargs)
381         self._settings[ConfWithStats.STATS_LOG_ENABLED] = \
382             compute_optional_conf(ConfWithStats.STATS_LOG_ENABLED,
383                                   ConfWithStats.DEFAULT_STATS_LOG_ENABLED,
384                                   **kwargs)
385         self._settings[ConfWithStats.STATS_TIME] = \
386             compute_optional_conf(ConfWithStats.STATS_TIME,
387                                   ConfWithStats.DEFAULT_STATS_TIME,
388                                   **kwargs)
389
390     @property
391     def stats_log_enabled(self):
392         return self._settings[ConfWithStats.STATS_LOG_ENABLED]
393
394     @stats_log_enabled.setter
395     def stats_log_enabled(self, enabled):
396         get_validator(ConfWithStats.STATS_LOG_ENABLED)(enabled)
397         if enabled != self.stats_log_enabled:
398             self._settings[ConfWithStats.STATS_LOG_ENABLED] = enabled
399             self._notify_listeners(ConfWithStats.UPDATE_STATS_LOG_ENABLED_EVT,
400                                    enabled)
401
402     @property
403     def stats_time(self):
404         return self._settings[ConfWithStats.STATS_TIME]
405
406     @stats_time.setter
407     def stats_time(self, stats_time):
408         get_validator(ConfWithStats.STATS_TIME)(stats_time)
409         if stats_time != self.stats_time:
410             self._settings[ConfWithStats.STATS_TIME] = stats_time
411             self._notify_listeners(ConfWithStats.UPDATE_STATS_TIME_EVT,
412                                    stats_time)
413
414     @classmethod
415     def get_opt_settings(cls):
416         confs = super(ConfWithStats, cls).get_opt_settings()
417         confs.update(ConfWithStats.OPTIONAL_SETTINGS)
418         return confs
419
420     @classmethod
421     def get_valid_evts(cls):
422         valid_evts = super(ConfWithStats, cls).get_valid_evts()
423         valid_evts.update(ConfWithStats.VALID_EVT)
424         return valid_evts
425
426     def update(self, **kwargs):
427         # Update inherited configurations
428         super(ConfWithStats, self).update(**kwargs)
429         self.stats_log_enabled = \
430             compute_optional_conf(ConfWithStats.STATS_LOG_ENABLED,
431                                   ConfWithStats.DEFAULT_STATS_LOG_ENABLED,
432                                   **kwargs)
433         self.stats_time = \
434             compute_optional_conf(ConfWithStats.STATS_TIME,
435                                   ConfWithStats.DEFAULT_STATS_TIME,
436                                   **kwargs)
437
438
439 @six.add_metaclass(ABCMeta)
440 class BaseConfListener(object):
441     """Base class of all configuration listeners."""
442
443     def __init__(self, base_conf):
444         pass
445     # TODO(PH): re-vist later and check if we need this check
446 #         if not isinstance(base_conf, BaseConf):
447 #             raise TypeError('Currently we only support listening to '
448 #                             'instances of BaseConf')
449
450
451 class ConfWithIdListener(BaseConfListener):
452
453     def __init__(self, conf_with_id):
454         assert conf_with_id
455         super(ConfWithIdListener, self).__init__(conf_with_id)
456         conf_with_id.add_listener(ConfWithId.UPDATE_NAME_EVT,
457                                   self.on_chg_name_conf_with_id)
458         conf_with_id.add_listener(ConfWithId.UPDATE_DESCRIPTION_EVT,
459                                   self.on_chg_desc_conf_with_id)
460
461     def on_chg_name_conf_with_id(self, conf_evt):
462         # Note did not makes this method abstract as this is not important
463         # event.
464         raise NotImplementedError()
465
466     def on_chg_desc_conf_with_id(self, conf_evt):
467         # Note did not makes this method abstract as this is not important
468         # event.
469         raise NotImplementedError()
470
471
472 class ConfWithStatsListener(BaseConfListener):
473
474     def __init__(self, conf_with_stats):
475         assert conf_with_stats
476         super(ConfWithStatsListener, self).__init__(conf_with_stats)
477         conf_with_stats.add_listener(
478             ConfWithStats.UPDATE_STATS_LOG_ENABLED_EVT,
479             self.on_chg_stats_enabled_conf_with_stats)
480
481         conf_with_stats.add_listener(ConfWithStats.UPDATE_STATS_TIME_EVT,
482                                      self.on_chg_stats_time_conf_with_stats)
483
484     @abstractmethod
485     def on_chg_stats_time_conf_with_stats(self, conf_evt):
486         raise NotImplementedError()
487
488     @abstractmethod
489     def on_chg_stats_enabled_conf_with_stats(self, conf_evt):
490         raise NotImplementedError()
491
492
493 @functools.total_ordering
494 class ConfEvent(object):
495     """Encapsulates configuration settings change/update event."""
496
497     def __init__(self, evt_src, evt_name, evt_value):
498         """Creates an instance using given parameters.
499
500         Parameters:
501             -`evt_src`: (BaseConf) source of the event
502             -`evt_name`: (str) name of event, has to be one of the valid
503             event of `evt_src`
504             - `evt_value`: (tuple) event context that helps event handler
505         """
506         if evt_name not in evt_src.get_valid_evts():
507             raise ValueError('Event %s is not a valid event for type %s.' %
508                              (evt_name, type(evt_src)))
509         self._src = evt_src
510         self._name = evt_name
511         self._value = evt_value
512
513     @property
514     def src(self):
515         return self._src
516
517     @property
518     def name(self):
519         return self._name
520
521     @property
522     def value(self):
523         return self._value
524
525     def __repr__(self):
526         return '<ConfEvent(%s, %s, %s)>' % (self.src, self.name, self.value)
527
528     def __str__(self):
529         return ('ConfEvent(src=%s, name=%s, value=%s)' %
530                 (self.src, self.name, self.value))
531
532     def __lt__(self, other):
533         return ((self.src, self.name, self.value) <
534                 (other.src, other.name, other.value))
535
536     def __eq__(self, other):
537         return ((self.src, self.name, self.value) ==
538                 (other.src, other.name, other.value))
539
540
541 # =============================================================================
542 # Runtime configuration setting validators and their registry.
543 # =============================================================================
544
545 @validate(name=ConfWithId.ID)
546 def validate_conf_id(identifier):
547     if not isinstance(identifier, str):
548         raise ConfigTypeError(conf_name=ConfWithId.ID, conf_value=identifier)
549     if len(identifier) > 128:
550         raise ConfigValueError(conf_name=ConfWithId.ID, conf_value=identifier)
551     return identifier
552
553
554 @validate(name=ConfWithId.NAME)
555 def validate_conf_name(name):
556     if not isinstance(name, str):
557         raise ConfigTypeError(conf_name=ConfWithId.NAME, conf_value=name)
558     if len(name) > 128:
559         raise ConfigValueError(conf_name=ConfWithId.NAME, conf_value=name)
560     return name
561
562
563 @validate(name=ConfWithId.DESCRIPTION)
564 def validate_conf_desc(description):
565     if not isinstance(description, str):
566         raise ConfigTypeError(conf_name=ConfWithId.DESCRIPTION,
567                               conf_value=description)
568     return description
569
570
571 @validate(name=ConfWithStats.STATS_LOG_ENABLED)
572 def validate_stats_log_enabled(stats_log_enabled):
573     if not isinstance(stats_log_enabled, bool):
574         raise ConfigTypeError(desc='Statistics log enabled settings can only'
575                               ' be boolean type.')
576     return stats_log_enabled
577
578
579 @validate(name=ConfWithStats.STATS_TIME)
580 def validate_stats_time(stats_time):
581     if not isinstance(stats_time, numbers.Integral):
582         raise ConfigTypeError(desc='Statistics log timer value has to be of '
583                               'integral type but got: %r' % stats_time)
584     if stats_time < 10:
585         raise ConfigValueError(desc='Statistics log timer cannot be set to '
586                                'less then 10 sec, given timer value %s.' %
587                                stats_time)
588     return stats_time
589
590
591 @validate(name=CAP_REFRESH)
592 def validate_cap_refresh(crefresh):
593     if not isinstance(crefresh, bool):
594         raise ConfigTypeError(desc='Invalid Refresh capability settings: %s. '
595                               'Boolean value expected' % crefresh)
596     return crefresh
597
598
599 @validate(name=CAP_ENHANCED_REFRESH)
600 def validate_cap_enhanced_refresh(cer):
601     if not isinstance(cer, bool):
602         raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
603                               'settings: %s. Boolean value expected' % cer)
604     return cer
605
606
607 @validate(name=CAP_FOUR_OCTET_AS_NUMBER)
608 def validate_cap_four_octet_as_number(cfoan):
609     if not isinstance(cfoan, bool):
610         raise ConfigTypeError(desc='Invalid Four-Octet AS Number capability '
611                               'settings: %s boolean value expected' % cfoan)
612     return cfoan
613
614
615 @validate(name=CAP_MBGP_IPV4)
616 def validate_cap_mbgp_ipv4(cmv4):
617     if not isinstance(cmv4, bool):
618         raise ConfigTypeError(desc='Invalid MP-BGP IPv4 capability '
619                               'settings: %s. Boolean value expected' % cmv4)
620
621     return cmv4
622
623
624 @validate(name=CAP_MBGP_IPV6)
625 def validate_cap_mbgp_ipv6(cmv6):
626     if not isinstance(cmv6, bool):
627         raise ConfigTypeError(desc='Invalid MP-BGP IPv6 capability '
628                               'settings: %s. Boolean value expected' % cmv6)
629
630     return cmv6
631
632
633 @validate(name=CAP_MBGP_VPNV4)
634 def validate_cap_mbgp_vpnv4(cmv4):
635     if not isinstance(cmv4, bool):
636         raise ConfigTypeError(desc='Invalid MP-BGP VPNv4 capability '
637                               'settings: %s. Boolean value expected' % cmv4)
638
639     return cmv4
640
641
642 @validate(name=CAP_MBGP_VPNV6)
643 def validate_cap_mbgp_vpnv6(cmv6):
644     if not isinstance(cmv6, bool):
645         raise ConfigTypeError(desc='Invalid MP-BGP VPNv6 capability '
646                               'settings: %s. Boolean value expected' % cmv6)
647
648     return cmv6
649
650
651 @validate(name=CAP_MBGP_EVPN)
652 def validate_cap_mbgp_evpn(cmevpn):
653     if not isinstance(cmevpn, bool):
654         raise ConfigTypeError(desc='Invalid Ethernet VPN capability '
655                               'settings: %s. Boolean value expected' % cmevpn)
656     return cmevpn
657
658
659 @validate(name=CAP_MBGP_IPV4FS)
660 def validate_cap_mbgp_ipv4fs(cmv4fs):
661     if not isinstance(cmv4fs, bool):
662         raise ConfigTypeError(desc='Invalid MP-BGP '
663                               'IPv4 Flow Specification capability '
664                               'settings: %s. Boolean value expected' % cmv4fs)
665     return cmv4fs
666
667
668 @validate(name=CAP_MBGP_IPV6FS)
669 def validate_cap_mbgp_ipv6fs(cmv6fs):
670     if not isinstance(cmv6fs, bool):
671         raise ConfigTypeError(desc='Invalid MP-BGP '
672                               'IPv6 Flow Specification capability '
673                               'settings: %s. Boolean value expected' % cmv6fs)
674     return cmv6fs
675
676
677 @validate(name=CAP_MBGP_VPNV4FS)
678 def validate_cap_mbgp_vpnv4fs(cmv4fs):
679     if not isinstance(cmv4fs, bool):
680         raise ConfigTypeError(desc='Invalid MP-BGP '
681                               'VPNv4 Flow Specification capability '
682                               'settings: %s. Boolean value expected' % cmv4fs)
683     return cmv4fs
684
685
686 @validate(name=CAP_MBGP_VPNV6FS)
687 def validate_cap_mbgp_vpnv66fs(cmv6fs):
688     if not isinstance(cmv6fs, bool):
689         raise ConfigTypeError(desc='Invalid MP-BGP '
690                               'VPNv6 Flow Specification capability '
691                               'settings: %s. Boolean value expected' % cmv6fs)
692     return cmv6fs
693
694
695 @validate(name=CAP_MBGP_L2VPNFS)
696 def validate_cap_mbgp_l2vpnfs(cml2fs):
697     if not isinstance(cml2fs, bool):
698         raise ConfigTypeError(desc='Invalid MP-BGP '
699                               'L2VPN Flow Specification capability '
700                               'settings: %s. Boolean value expected' % cml2fs)
701     return cml2fs
702
703
704 @validate(name=CAP_RTC)
705 def validate_cap_rtc(cap_rtc):
706     if not isinstance(cap_rtc, bool):
707         raise ConfigTypeError(desc='Invalid type for specifying RTC '
708                               'capability. Expected boolean got: %s' %
709                               type(cap_rtc))
710     return cap_rtc
711
712
713 @validate(name=RTC_AS)
714 def validate_cap_rtc_as(rtc_as):
715     if not is_valid_asn(rtc_as):
716         raise ConfigValueError(desc='Invalid RTC AS configuration value: %s'
717                                % rtc_as)
718     return rtc_as
719
720
721 @validate(name=HOLD_TIME)
722 def validate_hold_time(hold_time):
723     if ((hold_time is None) or (not isinstance(hold_time, int)) or
724             hold_time < 10):
725         raise ConfigValueError(desc='Invalid hold_time configuration value %s'
726                                % hold_time)
727
728     return hold_time
729
730
731 @validate(name=MULTI_EXIT_DISC)
732 def validate_med(med):
733     if med is not None and not validation.is_valid_med(med):
734         raise ConfigValueError(desc='Invalid multi-exit-discriminatory (med)'
735                                ' value: %s.' % med)
736     return med
737
738
739 @validate(name=SITE_OF_ORIGINS)
740 def validate_soo_list(soo_list):
741     if not isinstance(soo_list, list):
742         raise ConfigTypeError(conf_name=SITE_OF_ORIGINS, conf_value=soo_list)
743     if len(soo_list) > MAX_NUM_SOO:
744         raise ConfigValueError(desc='Max. SOO is limited to %s' %
745                                MAX_NUM_SOO)
746     if not all(validation.is_valid_ext_comm_attr(attr) for attr in soo_list):
747         raise ConfigValueError(conf_name=SITE_OF_ORIGINS,
748                                conf_value=soo_list)
749     # Check if we have duplicates
750     unique_rts = set(soo_list)
751     if len(unique_rts) != len(soo_list):
752         raise ConfigValueError(desc='Duplicate value provided in %s' %
753                                soo_list)
754     return soo_list
755
756
757 @validate(name=MAX_PREFIXES)
758 def validate_max_prefixes(max_prefixes):
759     if not isinstance(max_prefixes, six.integer_types):
760         raise ConfigTypeError(desc='Max. prefixes value should be of type '
761                               'int or long but found %s' % type(max_prefixes))
762     if max_prefixes < 0:
763         raise ConfigValueError(desc='Invalid max. prefixes value: %s' %
764                                max_prefixes)
765     return max_prefixes
766
767
768 @validate(name=ADVERTISE_PEER_AS)
769 def validate_advertise_peer_as(advertise_peer_as):
770     if not isinstance(advertise_peer_as, bool):
771         raise ConfigTypeError(desc='Invalid type for advertise-peer-as, '
772                               'expected bool got %s' %
773                               type(advertise_peer_as))
774     return advertise_peer_as
775
776
777 # =============================================================================
778 # Other utils.
779 # =============================================================================
780
781 def compute_optional_conf(conf_name, default_value, **all_config):
782     """Returns *conf_name* settings if provided in *all_config*, else returns
783      *default_value*.
784
785     Validates *conf_name* value if provided.
786     """
787     conf_value = all_config.get(conf_name)
788     if conf_value is not None:
789         # Validate configuration value.
790         conf_value = get_validator(conf_name)(conf_value)
791     else:
792         conf_value = default_value
793     return conf_value