1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 Running or runtime configuration base classes.
19 from abc import ABCMeta
20 from abc import abstractmethod
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
36 LOG = logging.getLogger('bgpspeaker.rtconf.base')
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'
56 HOLD_TIME = 'hold_time'
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'
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'
68 # MED - MULTI_EXIT_DISC
69 MULTI_EXIT_DISC = 'multi_exit_disc'
71 # Extended community attribute route origin.
72 SITE_OF_ORIGINS = 'site_of_origins'
74 # Constants related to errors.
75 CONF_NAME = 'conf_name'
76 CONF_VALUE = 'conf_value'
79 MAX_NUM_IMPORT_RT = 1000
80 MAX_NUM_EXPORT_RT = 250
84 # =============================================================================
85 # Runtime configuration errors or exceptions.
86 # =============================================================================
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.
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
103 def __init__(self, **kwargs):
104 conf_name = kwargs.get('conf_name')
106 super(MissingRequiredConf, self).__init__(
107 desc='Missing required configuration: %s' % conf_name)
109 super(MissingRequiredConf, self).__init__(desc=kwargs.get('desc'))
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.
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))
126 super(ConfigTypeError, self).__init__(
127 desc='Incorrect Type for configuration: %s' % conf_name)
129 super(ConfigTypeError, self).__init__(desc=kwargs.get('desc'))
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
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))
147 super(ConfigValueError, self).__init__(
148 desc='Incorrect Value for configuration: %s' % conf_name)
150 super(ConfigValueError, self).__init__(desc=kwargs.get('desc'))
153 # =============================================================================
154 # Configuration base classes.
155 # =============================================================================
157 @six.add_metaclass(ABCMeta)
158 class BaseConf(object):
159 """Base class for a set of configuration values.
161 Configurations can be required or optional. Also acts as a container of
162 configuration change listeners.
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()
172 # validate required and unknown settings
173 self._validate_req_unknown_settings(**kwargs)
175 # Initialize configuration settings.
176 self._init_req_settings(**kwargs)
177 self._init_opt_settings(**kwargs)
181 """Returns a copy of current settings."""
182 return self._settings.copy()
185 def get_valid_evts(cls):
189 def get_req_settings(cls):
193 def get_opt_settings(cls):
197 def _init_opt_settings(self, **kwargs):
198 """Sub-classes should override this method to initialize optional
204 def update(self, **kwargs):
205 # Validate given values
206 self._validate_req_unknown_settings(**kwargs)
208 def _validate_req_unknown_settings(self, **kwargs):
209 """Checks if required settings are present.
211 Also checks if unknown requirements are present.
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.')
218 given_attrs = frozenset(kwargs.keys())
219 unknown_attrs = given_attrs - self._all_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))
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
237 def add_listener(self, evt, callback):
238 # if (evt not in self.get_valid_evts()):
239 # raise RuntimeConfigError(desc=('Unknown event %s' % evt))
241 listeners = self._listeners.get(evt, None)
244 self._listeners[evt] = listeners
245 listeners.update([callback])
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)
256 def _notify_listeners(self, evt, value):
257 listeners = self._listeners.get(evt, [])
258 for callback in listeners:
259 callback(ConfEvent(self, evt, value))
262 return '%s(%r)' % (self.__class__, self._settings)
265 class ConfWithId(BaseConf):
266 """Configuration settings related to identity."""
267 # Config./resource identifier.
269 # Config./resource name.
271 # Config./resource description.
272 DESCRIPTION = 'description'
274 UPDATE_NAME_EVT = 'update_name_evt'
275 UPDATE_DESCRIPTION_EVT = 'update_description_evt'
277 VALID_EVT = frozenset([UPDATE_NAME_EVT, UPDATE_DESCRIPTION_EVT])
278 OPTIONAL_SETTINGS = frozenset([ID, NAME, DESCRIPTION])
280 def __init__(self, **kwargs):
281 super(ConfWithId, self).__init__(**kwargs)
284 def get_opt_settings(cls):
285 self_confs = super(ConfWithId, cls).get_opt_settings()
286 self_confs.update(ConfWithId.OPTIONAL_SETTINGS)
290 def get_req_settings(cls):
291 self_confs = super(ConfWithId, cls).get_req_settings()
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
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)
311 return self._settings[ConfWithId.ID]
315 return self._settings[ConfWithId.NAME]
318 def name(self, new_name):
321 new_name = repr(self)
323 get_validator(ConfWithId.NAME)(new_name)
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))
331 def description(self):
332 return self._settings[ConfWithId.DESCRIPTION]
335 def description(self, new_description):
336 old_desc = self.description
337 if not new_description:
338 new_description = str(self)
340 get_validator(ConfWithId.DESCRIPTION)(new_description)
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))
347 def update(self, **kwargs):
348 # Update inherited configurations
349 super(ConfWithId, self).update(**kwargs)
350 self.name = compute_optional_conf(ConfWithId.NAME,
353 self.description = compute_optional_conf(ConfWithId.DESCRIPTION,
358 class ConfWithStats(BaseConf):
359 """Configuration settings related to statistics collection."""
361 # Enable or disable statistics logging.
362 STATS_LOG_ENABLED = 'statistics_log_enabled'
363 DEFAULT_STATS_LOG_ENABLED = False
365 # Statistics logging time.
366 STATS_TIME = 'statistics_interval'
367 DEFAULT_STATS_TIME = 60
369 UPDATE_STATS_LOG_ENABLED_EVT = 'update_stats_log_enabled_evt'
370 UPDATE_STATS_TIME_EVT = 'update_stats_time_evt'
372 VALID_EVT = frozenset([UPDATE_STATS_LOG_ENABLED_EVT,
373 UPDATE_STATS_TIME_EVT])
374 OPTIONAL_SETTINGS = frozenset([STATS_LOG_ENABLED, STATS_TIME])
376 def __init__(self, **kwargs):
377 super(ConfWithStats, self).__init__(**kwargs)
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,
385 self._settings[ConfWithStats.STATS_TIME] = \
386 compute_optional_conf(ConfWithStats.STATS_TIME,
387 ConfWithStats.DEFAULT_STATS_TIME,
391 def stats_log_enabled(self):
392 return self._settings[ConfWithStats.STATS_LOG_ENABLED]
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,
403 def stats_time(self):
404 return self._settings[ConfWithStats.STATS_TIME]
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,
415 def get_opt_settings(cls):
416 confs = super(ConfWithStats, cls).get_opt_settings()
417 confs.update(ConfWithStats.OPTIONAL_SETTINGS)
421 def get_valid_evts(cls):
422 valid_evts = super(ConfWithStats, cls).get_valid_evts()
423 valid_evts.update(ConfWithStats.VALID_EVT)
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,
434 compute_optional_conf(ConfWithStats.STATS_TIME,
435 ConfWithStats.DEFAULT_STATS_TIME,
439 @six.add_metaclass(ABCMeta)
440 class BaseConfListener(object):
441 """Base class of all configuration listeners."""
443 def __init__(self, base_conf):
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')
451 class ConfWithIdListener(BaseConfListener):
453 def __init__(self, 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)
461 def on_chg_name_conf_with_id(self, conf_evt):
462 # Note did not makes this method abstract as this is not important
464 raise NotImplementedError()
466 def on_chg_desc_conf_with_id(self, conf_evt):
467 # Note did not makes this method abstract as this is not important
469 raise NotImplementedError()
472 class ConfWithStatsListener(BaseConfListener):
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)
481 conf_with_stats.add_listener(ConfWithStats.UPDATE_STATS_TIME_EVT,
482 self.on_chg_stats_time_conf_with_stats)
485 def on_chg_stats_time_conf_with_stats(self, conf_evt):
486 raise NotImplementedError()
489 def on_chg_stats_enabled_conf_with_stats(self, conf_evt):
490 raise NotImplementedError()
493 @functools.total_ordering
494 class ConfEvent(object):
495 """Encapsulates configuration settings change/update event."""
497 def __init__(self, evt_src, evt_name, evt_value):
498 """Creates an instance using given parameters.
501 -`evt_src`: (BaseConf) source of the event
502 -`evt_name`: (str) name of event, has to be one of the valid
504 - `evt_value`: (tuple) event context that helps event handler
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)))
510 self._name = evt_name
511 self._value = evt_value
526 return '<ConfEvent(%s, %s, %s)>' % (self.src, self.name, self.value)
529 return ('ConfEvent(src=%s, name=%s, value=%s)' %
530 (self.src, self.name, self.value))
532 def __lt__(self, other):
533 return ((self.src, self.name, self.value) <
534 (other.src, other.name, other.value))
536 def __eq__(self, other):
537 return ((self.src, self.name, self.value) ==
538 (other.src, other.name, other.value))
541 # =============================================================================
542 # Runtime configuration setting validators and their registry.
543 # =============================================================================
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)
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)
559 raise ConfigValueError(conf_name=ConfWithId.NAME, conf_value=name)
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)
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'
576 return stats_log_enabled
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)
585 raise ConfigValueError(desc='Statistics log timer cannot be set to '
586 'less then 10 sec, given timer value %s.' %
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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' %
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'
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
725 raise ConfigValueError(desc='Invalid hold_time configuration value %s'
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)'
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' %
746 if not all(validation.is_valid_ext_comm_attr(attr) for attr in soo_list):
747 raise ConfigValueError(conf_name=SITE_OF_ORIGINS,
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' %
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))
763 raise ConfigValueError(desc='Invalid max. prefixes value: %s' %
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
777 # =============================================================================
779 # =============================================================================
781 def compute_optional_conf(conf_name, default_value, **all_config):
782 """Returns *conf_name* settings if provided in *all_config*, else returns
785 Validates *conf_name* value if provided.
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)
792 conf_value = default_value