723 lines
25 KiB
Python
723 lines
25 KiB
Python
# 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 base classes.
|
|
"""
|
|
from abc import ABCMeta
|
|
from abc import abstractmethod
|
|
import functools
|
|
import numbers
|
|
import logging
|
|
import six
|
|
import uuid
|
|
|
|
from ryu.services.protocols.bgp.base import add_bgp_error_metadata
|
|
from ryu.services.protocols.bgp.base import BGPSException
|
|
from ryu.services.protocols.bgp.base import get_validator
|
|
from ryu.services.protocols.bgp.base import RUNTIME_CONF_ERROR_CODE
|
|
from ryu.services.protocols.bgp.base import validate
|
|
from ryu.services.protocols.bgp.utils import validation
|
|
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
|
|
|
|
LOG = logging.getLogger('bgpspeaker.rtconf.base')
|
|
|
|
#
|
|
# Nested settings.
|
|
#
|
|
CAP_REFRESH = 'cap_refresh'
|
|
CAP_ENHANCED_REFRESH = 'cap_enhanced_refresh'
|
|
CAP_MBGP_IPV4 = 'cap_mbgp_ipv4'
|
|
CAP_MBGP_IPV6 = 'cap_mbgp_ipv6'
|
|
CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4'
|
|
CAP_MBGP_VPNV6 = 'cap_mbgp_vpnv6'
|
|
CAP_RTC = 'cap_rtc'
|
|
RTC_AS = 'rtc_as'
|
|
HOLD_TIME = 'hold_time'
|
|
|
|
# To control how many prefixes can be received from a neighbor.
|
|
# 0 value indicates no limit and other related options will be ignored.
|
|
# Current behavior is to log that limit has reached.
|
|
MAX_PREFIXES = 'max_prefixes'
|
|
|
|
# Has same meaning as: http://www.juniper.net/techpubs/software/junos/junos94
|
|
# /swconfig-routing/disabling-suppression-of-route-
|
|
# advertisements.html#id-13255463
|
|
ADVERTISE_PEER_AS = 'advertise_peer_as'
|
|
|
|
# MED - MULTI_EXIT_DISC
|
|
MULTI_EXIT_DISC = 'multi_exit_disc'
|
|
|
|
# Extended community attribute route origin.
|
|
SITE_OF_ORIGINS = 'site_of_origins'
|
|
|
|
# Constants related to errors.
|
|
CONF_NAME = 'conf_name'
|
|
CONF_VALUE = 'conf_value'
|
|
|
|
# Max. value limits
|
|
MAX_NUM_IMPORT_RT = 1000
|
|
MAX_NUM_EXPORT_RT = 250
|
|
MAX_NUM_SOO = 10
|
|
|
|
|
|
# =============================================================================
|
|
# Runtime configuration errors or exceptions.
|
|
# =============================================================================
|
|
|
|
@add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=1,
|
|
def_desc='Error with runtime-configuration.')
|
|
class RuntimeConfigError(BGPSException):
|
|
"""Base class for all runtime configuration errors.
|
|
"""
|
|
pass
|
|
|
|
|
|
@add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=2,
|
|
def_desc='Missing required configuration.')
|
|
class MissingRequiredConf(RuntimeConfigError):
|
|
"""Exception raised when trying to configure with missing required
|
|
settings.
|
|
"""
|
|
def __init__(self, **kwargs):
|
|
conf_name = kwargs.get('conf_name')
|
|
if conf_name:
|
|
super(MissingRequiredConf, self).__init__(
|
|
desc='Missing required configuration: %s' % conf_name)
|
|
else:
|
|
super(MissingRequiredConf, self).__init__(desc=kwargs.get('desc'))
|
|
|
|
|
|
@add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=3,
|
|
def_desc='Incorrect Type for configuration.')
|
|
class ConfigTypeError(RuntimeConfigError):
|
|
"""Exception raised when configuration value type miss-match happens.
|
|
"""
|
|
def __init__(self, **kwargs):
|
|
conf_name = kwargs.get(CONF_NAME)
|
|
conf_value = kwargs.get(CONF_VALUE)
|
|
if conf_name and conf_value:
|
|
super(ConfigTypeError, self).__init__(
|
|
desc='Incorrect Type %s for configuration: %s' %
|
|
(conf_value, conf_name))
|
|
elif conf_name:
|
|
super(ConfigTypeError, self).__init__(
|
|
desc='Incorrect Type for configuration: %s' % conf_name)
|
|
else:
|
|
super(ConfigTypeError, self).__init__(desc=kwargs.get('desc'))
|
|
|
|
|
|
@add_bgp_error_metadata(code=RUNTIME_CONF_ERROR_CODE, sub_code=4,
|
|
def_desc='Incorrect Value for configuration.')
|
|
class ConfigValueError(RuntimeConfigError):
|
|
"""Exception raised when configuration value is of correct type but
|
|
incorrect value.
|
|
"""
|
|
def __init__(self, **kwargs):
|
|
conf_name = kwargs.get(CONF_NAME)
|
|
conf_value = kwargs.get(CONF_VALUE)
|
|
if conf_name and conf_value:
|
|
super(ConfigValueError, self).__init__(
|
|
desc='Incorrect Value %s for configuration: %s' %
|
|
(conf_value, conf_name))
|
|
elif conf_name:
|
|
super(ConfigValueError, self).__init__(
|
|
desc='Incorrect Value for configuration: %s' % conf_name)
|
|
else:
|
|
super(ConfigValueError, self).__init__(desc=kwargs.get('desc'))
|
|
|
|
|
|
# =============================================================================
|
|
# Configuration base classes.
|
|
# =============================================================================
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class BaseConf(object):
|
|
"""Base class for a set of configuration values.
|
|
|
|
Configurations can be required or optional. Also acts as a container of
|
|
configuration change listeners.
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
self._req_settings = self.get_req_settings()
|
|
self._opt_settings = self.get_opt_settings()
|
|
self._valid_evts = self.get_valid_evts()
|
|
self._listeners = {}
|
|
self._settings = {}
|
|
|
|
# validate required and unknown settings
|
|
self._validate_req_unknown_settings(**kwargs)
|
|
|
|
# Initialize configuration settings.
|
|
self._init_req_settings(**kwargs)
|
|
self._init_opt_settings(**kwargs)
|
|
|
|
@property
|
|
def settings(self):
|
|
"""Returns a copy of current settings."""
|
|
return self._settings.copy()
|
|
|
|
@classmethod
|
|
def get_valid_evts(self):
|
|
return set()
|
|
|
|
@classmethod
|
|
def get_req_settings(self):
|
|
return set()
|
|
|
|
@classmethod
|
|
def get_opt_settings(self):
|
|
return set()
|
|
|
|
@abstractmethod
|
|
def _init_opt_settings(self, **kwargs):
|
|
"""Sub-classes should override this method to initialize optional
|
|
settings.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update(self, **kwargs):
|
|
# Validate given values
|
|
self._validate_req_unknown_settings(**kwargs)
|
|
|
|
def _validate_req_unknown_settings(self, **kwargs):
|
|
"""Checks if required settings are present.
|
|
|
|
Also checks if unknown requirements are present.
|
|
"""
|
|
# Validate given configuration.
|
|
self._all_attrs = (self._req_settings | self._opt_settings)
|
|
if not kwargs and len(self._req_settings) > 0:
|
|
raise MissingRequiredConf(desc='Missing all required attributes.')
|
|
|
|
given_attrs = frozenset(kwargs.keys())
|
|
unknown_attrs = given_attrs - self._all_attrs
|
|
if unknown_attrs:
|
|
raise RuntimeConfigError(desc=(
|
|
'Unknown attributes: %s' %
|
|
', '.join([str(i) for i in unknown_attrs]))
|
|
)
|
|
missing_req_settings = self._req_settings - given_attrs
|
|
if missing_req_settings:
|
|
raise MissingRequiredConf(conf_name=list(missing_req_settings))
|
|
|
|
def _init_req_settings(self, **kwargs):
|
|
for req_attr in self._req_settings:
|
|
req_attr_value = kwargs.get(req_attr)
|
|
if req_attr_value is None:
|
|
raise MissingRequiredConf(conf_name=req_attr_value)
|
|
# Validate attribute value
|
|
req_attr_value = get_validator(req_attr)(req_attr_value)
|
|
self._settings[req_attr] = req_attr_value
|
|
|
|
def add_listener(self, evt, callback):
|
|
# if (evt not in self.get_valid_evts()):
|
|
# raise RuntimeConfigError(desc=('Unknown event %s' % evt))
|
|
|
|
listeners = self._listeners.get(evt, None)
|
|
if not listeners:
|
|
listeners = set()
|
|
self._listeners[evt] = listeners
|
|
listeners.update([callback])
|
|
|
|
def remove_listener(self, evt, callback):
|
|
if evt in self.get_valid_evts():
|
|
listeners = self._listeners.get(evt, None)
|
|
if listeners and (callback in listeners):
|
|
listeners.remove(callback)
|
|
return True
|
|
|
|
return False
|
|
|
|
def _notify_listeners(self, evt, value):
|
|
listeners = self._listeners.get(evt, [])
|
|
for callback in listeners:
|
|
callback(ConfEvent(self, evt, value))
|
|
|
|
def __repr__(self):
|
|
return '%s(%r)' % (self.__class__, self._settings)
|
|
|
|
|
|
class ConfWithId(BaseConf):
|
|
"""Configuration settings related to identity."""
|
|
# Config./resource identifier.
|
|
ID = 'id'
|
|
# Config./resource name.
|
|
NAME = 'name'
|
|
# Config./resource description.
|
|
DESCRIPTION = 'description'
|
|
|
|
UPDATE_NAME_EVT = 'update_name_evt'
|
|
UPDATE_DESCRIPTION_EVT = 'update_description_evt'
|
|
|
|
VALID_EVT = frozenset([UPDATE_NAME_EVT, UPDATE_DESCRIPTION_EVT])
|
|
OPTIONAL_SETTINGS = frozenset([ID, NAME, DESCRIPTION])
|
|
|
|
def __init__(self, **kwargs):
|
|
super(ConfWithId, self).__init__(**kwargs)
|
|
|
|
@classmethod
|
|
def get_opt_settings(cls):
|
|
self_confs = super(ConfWithId, cls).get_opt_settings()
|
|
self_confs.update(ConfWithId.OPTIONAL_SETTINGS)
|
|
return self_confs
|
|
|
|
@classmethod
|
|
def get_req_settings(cls):
|
|
self_confs = super(ConfWithId, cls).get_req_settings()
|
|
return self_confs
|
|
|
|
@classmethod
|
|
def get_valid_evts(cls):
|
|
self_valid_evts = super(ConfWithId, cls).get_valid_evts()
|
|
self_valid_evts.update(ConfWithId.VALID_EVT)
|
|
return self_valid_evts
|
|
|
|
def _init_opt_settings(self, **kwargs):
|
|
super(ConfWithId, self)._init_opt_settings(**kwargs)
|
|
self._settings[ConfWithId.ID] = \
|
|
compute_optional_conf(ConfWithId.ID, str(uuid.uuid4()), **kwargs)
|
|
self._settings[ConfWithId.NAME] = \
|
|
compute_optional_conf(ConfWithId.NAME, str(self), **kwargs)
|
|
self._settings[ConfWithId.DESCRIPTION] = \
|
|
compute_optional_conf(ConfWithId.DESCRIPTION, str(self), **kwargs)
|
|
|
|
@property
|
|
def id(self):
|
|
return self._settings[ConfWithId.ID]
|
|
|
|
@property
|
|
def name(self):
|
|
return self._settings[ConfWithId.NAME]
|
|
|
|
@name.setter
|
|
def name(self, new_name):
|
|
old_name = self.name
|
|
if not new_name:
|
|
new_name = repr(self)
|
|
else:
|
|
get_validator(ConfWithId.NAME)(new_name)
|
|
|
|
if old_name != new_name:
|
|
self._settings[ConfWithId.NAME] = new_name
|
|
self._notify_listeners(ConfWithId.UPDATE_NAME_EVT,
|
|
(old_name, self.name))
|
|
|
|
@property
|
|
def description(self):
|
|
return self._settings[ConfWithId.DESCRIPTION]
|
|
|
|
@description.setter
|
|
def description(self, new_description):
|
|
old_desc = self.description
|
|
if not new_description:
|
|
new_description = str(self)
|
|
else:
|
|
get_validator(ConfWithId.DESCRIPTION)(new_description)
|
|
|
|
if old_desc != new_description:
|
|
self._settings[ConfWithId.DESCRIPTION] = new_description
|
|
self._notify_listeners(ConfWithId.UPDATE_DESCRIPTION_EVT,
|
|
(old_desc, self.description))
|
|
|
|
def update(self, **kwargs):
|
|
# Update inherited configurations
|
|
super(ConfWithId, self).update(**kwargs)
|
|
self.name = compute_optional_conf(ConfWithId.NAME,
|
|
str(self),
|
|
**kwargs)
|
|
self.description = compute_optional_conf(ConfWithId.DESCRIPTION,
|
|
str(self),
|
|
**kwargs)
|
|
|
|
|
|
class ConfWithStats(BaseConf):
|
|
"""Configuration settings related to statistics collection."""
|
|
|
|
# Enable or disable statistics logging.
|
|
STATS_LOG_ENABLED = 'statistics_log_enabled'
|
|
DEFAULT_STATS_LOG_ENABLED = False
|
|
|
|
# Statistics logging time.
|
|
STATS_TIME = 'statistics_interval'
|
|
DEFAULT_STATS_TIME = 60
|
|
|
|
UPDATE_STATS_LOG_ENABLED_EVT = 'update_stats_log_enabled_evt'
|
|
UPDATE_STATS_TIME_EVT = 'update_stats_time_evt'
|
|
|
|
VALID_EVT = frozenset([UPDATE_STATS_LOG_ENABLED_EVT,
|
|
UPDATE_STATS_TIME_EVT])
|
|
OPTIONAL_SETTINGS = frozenset([STATS_LOG_ENABLED, STATS_TIME])
|
|
|
|
def __init__(self, **kwargs):
|
|
super(ConfWithStats, self).__init__(**kwargs)
|
|
|
|
def _init_opt_settings(self, **kwargs):
|
|
super(ConfWithStats, self)._init_opt_settings(**kwargs)
|
|
self._settings[ConfWithStats.STATS_LOG_ENABLED] = \
|
|
compute_optional_conf(ConfWithStats.STATS_LOG_ENABLED,
|
|
ConfWithStats.DEFAULT_STATS_LOG_ENABLED,
|
|
**kwargs)
|
|
self._settings[ConfWithStats.STATS_TIME] = \
|
|
compute_optional_conf(ConfWithStats.STATS_TIME,
|
|
ConfWithStats.DEFAULT_STATS_TIME,
|
|
**kwargs)
|
|
|
|
@property
|
|
def stats_log_enabled(self):
|
|
return self._settings[ConfWithStats.STATS_LOG_ENABLED]
|
|
|
|
@stats_log_enabled.setter
|
|
def stats_log_enabled(self, enabled):
|
|
get_validator(ConfWithStats.STATS_LOG_ENABLED)(enabled)
|
|
if enabled != self.stats_log_enabled:
|
|
self._settings[ConfWithStats.STATS_LOG_ENABLED] = enabled
|
|
self._notify_listeners(ConfWithStats.UPDATE_STATS_LOG_ENABLED_EVT,
|
|
enabled)
|
|
|
|
@property
|
|
def stats_time(self):
|
|
return self._settings[ConfWithStats.STATS_TIME]
|
|
|
|
@stats_time.setter
|
|
def stats_time(self, stats_time):
|
|
get_validator(ConfWithStats.STATS_TIME)(stats_time)
|
|
if stats_time != self.stats_time:
|
|
self._settings[ConfWithStats.STATS_TIME] = stats_time
|
|
self._notify_listeners(ConfWithStats.UPDATE_STATS_TIME_EVT,
|
|
stats_time)
|
|
|
|
@classmethod
|
|
def get_opt_settings(cls):
|
|
confs = super(ConfWithStats, cls).get_opt_settings()
|
|
confs.update(ConfWithStats.OPTIONAL_SETTINGS)
|
|
return confs
|
|
|
|
@classmethod
|
|
def get_valid_evts(cls):
|
|
valid_evts = super(ConfWithStats, cls).get_valid_evts()
|
|
valid_evts.update(ConfWithStats.VALID_EVT)
|
|
return valid_evts
|
|
|
|
def update(self, **kwargs):
|
|
# Update inherited configurations
|
|
super(ConfWithStats, self).update(**kwargs)
|
|
self.stats_log_enabled = \
|
|
compute_optional_conf(ConfWithStats.STATS_LOG_ENABLED,
|
|
ConfWithStats.DEFAULT_STATS_LOG_ENABLED,
|
|
**kwargs)
|
|
self.stats_time = \
|
|
compute_optional_conf(ConfWithStats.STATS_TIME,
|
|
ConfWithStats.DEFAULT_STATS_TIME,
|
|
**kwargs)
|
|
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class BaseConfListener(object):
|
|
"""Base class of all configuration listeners."""
|
|
|
|
def __init__(self, base_conf):
|
|
pass
|
|
# TODO(PH): re-vist later and check if we need this check
|
|
# if not isinstance(base_conf, BaseConf):
|
|
# raise TypeError('Currently we only support listening to '
|
|
# 'instances of BaseConf')
|
|
|
|
|
|
class ConfWithIdListener(BaseConfListener):
|
|
|
|
def __init__(self, conf_with_id):
|
|
assert conf_with_id
|
|
super(ConfWithIdListener, self).__init__(conf_with_id)
|
|
conf_with_id.add_listener(ConfWithId.UPDATE_NAME_EVT,
|
|
self.on_chg_name_conf_with_id)
|
|
conf_with_id.add_listener(ConfWithId.UPDATE_DESCRIPTION_EVT,
|
|
self.on_chg_desc_conf_with_id)
|
|
|
|
def on_chg_name_conf_with_id(self, conf_evt):
|
|
# Note did not makes this method abstract as this is not important
|
|
# event.
|
|
raise NotImplementedError()
|
|
|
|
def on_chg_desc_conf_with_id(self, conf_evt):
|
|
# Note did not makes this method abstract as this is not important
|
|
# event.
|
|
raise NotImplementedError()
|
|
|
|
|
|
class ConfWithStatsListener(BaseConfListener):
|
|
|
|
def __init__(self, conf_with_stats):
|
|
assert conf_with_stats
|
|
super(ConfWithStatsListener, self).__init__(conf_with_stats)
|
|
conf_with_stats.add_listener(
|
|
ConfWithStats.UPDATE_STATS_LOG_ENABLED_EVT,
|
|
self.on_chg_stats_enabled_conf_with_stats)
|
|
|
|
conf_with_stats.add_listener(ConfWithStats.UPDATE_STATS_TIME_EVT,
|
|
self.on_chg_stats_time_conf_with_stats)
|
|
|
|
@abstractmethod
|
|
def on_chg_stats_time_conf_with_stats(self, conf_evt):
|
|
raise NotImplementedError()
|
|
|
|
@abstractmethod
|
|
def on_chg_stats_enabled_conf_with_stats(self, conf_evt):
|
|
raise NotImplementedError()
|
|
|
|
|
|
@functools.total_ordering
|
|
class ConfEvent(object):
|
|
"""Encapsulates configuration settings change/update event."""
|
|
|
|
def __init__(self, evt_src, evt_name, evt_value):
|
|
"""Creates an instance using given parameters.
|
|
|
|
Parameters:
|
|
-`evt_src`: (BaseConf) source of the event
|
|
-`evt_name`: (str) name of event, has to be one of the valid
|
|
event of `evt_src`
|
|
- `evt_value`: (tuple) event context that helps event handler
|
|
"""
|
|
if evt_name not in evt_src.get_valid_evts():
|
|
raise ValueError('Event %s is not a valid event for type %s.' %
|
|
(evt_name, type(evt_src)))
|
|
self._src = evt_src
|
|
self._name = evt_name
|
|
self._value = evt_value
|
|
|
|
@property
|
|
def src(self):
|
|
return self._src
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
@property
|
|
def value(self):
|
|
return self._value
|
|
|
|
def __repr__(self):
|
|
return '<ConfEvent(%s, %s, %s)>' % (self.src, self.name, self.value)
|
|
|
|
def __str__(self):
|
|
return ('ConfEvent(src=%s, name=%s, value=%s)' %
|
|
(self.src, self.name, self.value))
|
|
|
|
def __lt__(self, other):
|
|
return ((self.src, self.name, self.value) <
|
|
(other.src, other.name, other.value))
|
|
|
|
def __eq__(self, other):
|
|
return ((self.src, self.name, self.value) ==
|
|
(other.src, other.name, other.value))
|
|
|
|
|
|
# =============================================================================
|
|
# Runtime configuration setting validators and their registry.
|
|
# =============================================================================
|
|
|
|
@validate(name=ConfWithId.ID)
|
|
def validate_conf_id(identifier):
|
|
if not isinstance(identifier, str):
|
|
raise ConfigTypeError(conf_name=ConfWithId.ID, conf_value=identifier)
|
|
if len(identifier) > 128:
|
|
raise ConfigValueError(conf_name=ConfWithId.ID, conf_value=identifier)
|
|
return identifier
|
|
|
|
|
|
@validate(name=ConfWithId.NAME)
|
|
def validate_conf_name(name):
|
|
if not isinstance(name, str):
|
|
raise ConfigTypeError(conf_name=ConfWithId.NAME, conf_value=name)
|
|
if len(name) > 128:
|
|
raise ConfigValueError(conf_name=ConfWithId.NAME, conf_value=name)
|
|
return name
|
|
|
|
|
|
@validate(name=ConfWithId.DESCRIPTION)
|
|
def validate_conf_desc(description):
|
|
if not isinstance(description, str):
|
|
raise ConfigTypeError(conf_name=ConfWithId.DESCRIPTION,
|
|
conf_value=description)
|
|
return description
|
|
|
|
|
|
@validate(name=ConfWithStats.STATS_LOG_ENABLED)
|
|
def validate_stats_log_enabled(stats_log_enabled):
|
|
if stats_log_enabled not in (True, False):
|
|
raise ConfigTypeError(desc='Statistics log enabled settings can only'
|
|
' be boolean type.')
|
|
return stats_log_enabled
|
|
|
|
|
|
@validate(name=ConfWithStats.STATS_TIME)
|
|
def validate_stats_time(stats_time):
|
|
if not isinstance(stats_time, numbers.Integral):
|
|
raise ConfigTypeError(desc='Statistics log timer value has to be of '
|
|
'integral type but got: %r' % stats_time)
|
|
if stats_time < 10:
|
|
raise ConfigValueError(desc='Statistics log timer cannot be set to '
|
|
'less then 10 sec, given timer value %s.' %
|
|
stats_time)
|
|
return stats_time
|
|
|
|
|
|
@validate(name=CAP_REFRESH)
|
|
def validate_cap_refresh(crefresh):
|
|
if crefresh not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid Refresh capability settings: %s '
|
|
' boolean value expected' % crefresh)
|
|
return crefresh
|
|
|
|
|
|
@validate(name=CAP_ENHANCED_REFRESH)
|
|
def validate_cap_enhanced_refresh(cer):
|
|
if cer not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
|
'settings: %s boolean value expected' % cer)
|
|
return cer
|
|
|
|
|
|
@validate(name=CAP_MBGP_IPV4)
|
|
def validate_cap_mbgp_ipv4(cmv4):
|
|
if cmv4 not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
|
'settings: %s boolean value expected' % cmv4)
|
|
|
|
return cmv4
|
|
|
|
|
|
@validate(name=CAP_MBGP_IPV6)
|
|
def validate_cap_mbgp_ipv6(cmv6):
|
|
if cmv6 not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
|
'settings: %s boolean value expected' % cmv6)
|
|
|
|
return cmv6
|
|
|
|
|
|
@validate(name=CAP_MBGP_VPNV4)
|
|
def validate_cap_mbgp_vpnv4(cmv4):
|
|
if cmv4 not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
|
'settings: %s boolean value expected' % cmv4)
|
|
|
|
return cmv4
|
|
|
|
|
|
@validate(name=CAP_MBGP_VPNV6)
|
|
def validate_cap_mbgp_vpnv6(cmv6):
|
|
if cmv6 not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
|
'settings: %s boolean value expected' % cmv6)
|
|
|
|
return cmv6
|
|
|
|
|
|
@validate(name=CAP_RTC)
|
|
def validate_cap_rtc(cap_rtc):
|
|
if cap_rtc not in (True, False):
|
|
raise ConfigTypeError(desc='Invalid type for specifying RTC '
|
|
'capability. Expected boolean got: %s' %
|
|
type(cap_rtc))
|
|
return cap_rtc
|
|
|
|
|
|
@validate(name=RTC_AS)
|
|
def validate_cap_rtc_as(rtc_as):
|
|
if not is_valid_old_asn(rtc_as):
|
|
raise ConfigValueError(desc='Invalid RTC AS configuration value: %s'
|
|
% rtc_as)
|
|
return rtc_as
|
|
|
|
|
|
@validate(name=HOLD_TIME)
|
|
def validate_hold_time(hold_time):
|
|
if ((hold_time is None) or (not isinstance(hold_time, int)) or
|
|
hold_time < 10):
|
|
raise ConfigValueError(desc='Invalid hold_time configuration value %s'
|
|
% hold_time)
|
|
|
|
return hold_time
|
|
|
|
|
|
@validate(name=MULTI_EXIT_DISC)
|
|
def validate_med(med):
|
|
if med is not None and not validation.is_valid_med(med):
|
|
raise ConfigValueError(desc='Invalid multi-exit-discriminatory (med)'
|
|
' value: %s.' % med)
|
|
return med
|
|
|
|
|
|
@validate(name=SITE_OF_ORIGINS)
|
|
def validate_soo_list(soo_list):
|
|
if not isinstance(soo_list, list):
|
|
raise ConfigTypeError(conf_name=SITE_OF_ORIGINS, conf_value=soo_list)
|
|
if not (len(soo_list) <= MAX_NUM_SOO):
|
|
raise ConfigValueError(desc='Max. SOO is limited to %s' %
|
|
MAX_NUM_SOO)
|
|
if not all(validation.is_valid_ext_comm_attr(attr) for attr in soo_list):
|
|
raise ConfigValueError(conf_name=SITE_OF_ORIGINS,
|
|
conf_value=soo_list)
|
|
# Check if we have duplicates
|
|
unique_rts = set(soo_list)
|
|
if len(unique_rts) != len(soo_list):
|
|
raise ConfigValueError(desc='Duplicate value provided in %s' %
|
|
(soo_list))
|
|
return soo_list
|
|
|
|
|
|
@validate(name=MAX_PREFIXES)
|
|
def validate_max_prefixes(max_prefixes):
|
|
if not isinstance(max_prefixes, six.integer_types):
|
|
raise ConfigTypeError(desc='Max. prefixes value should be of type '
|
|
'int or long but found %s' % type(max_prefixes))
|
|
if max_prefixes < 0:
|
|
raise ConfigValueError(desc='Invalid max. prefixes value: %s' %
|
|
max_prefixes)
|
|
return max_prefixes
|
|
|
|
|
|
@validate(name=ADVERTISE_PEER_AS)
|
|
def validate_advertise_peer_as(advertise_peer_as):
|
|
if not isinstance(advertise_peer_as, bool):
|
|
raise ConfigTypeError(desc='Invalid type for advertise-peer-as, '
|
|
'expected bool got %s' %
|
|
type(advertise_peer_as))
|
|
return advertise_peer_as
|
|
|
|
|
|
# =============================================================================
|
|
# Other utils.
|
|
# =============================================================================
|
|
|
|
def compute_optional_conf(conf_name, default_value, **all_config):
|
|
"""Returns *conf_name* settings if provided in *all_config*, else returns
|
|
*default_value*.
|
|
|
|
Validates *conf_name* value if provided.
|
|
"""
|
|
conf_value = all_config.get(conf_name)
|
|
if conf_value is not None:
|
|
# Validate configuration value.
|
|
conf_value = get_validator(conf_name)(conf_value)
|
|
else:
|
|
conf_value = default_value
|
|
return conf_value
|