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""" 19from abc import ABCMeta 20from abc import abstractmethod 21import functools 22import numbers 23import logging 24import uuid 25 26import six 27 28from ryu.services.protocols.bgp.base import add_bgp_error_metadata 29from ryu.services.protocols.bgp.base import BGPSException 30from ryu.services.protocols.bgp.base import get_validator 31from ryu.services.protocols.bgp.base import RUNTIME_CONF_ERROR_CODE 32from ryu.services.protocols.bgp.base import validate 33from ryu.services.protocols.bgp.utils import validation 34from ryu.services.protocols.bgp.utils.validation import is_valid_asn 35 36LOG = logging.getLogger('bgpspeaker.rtconf.base') 37 38# 39# Nested settings. 40# 41CAP_REFRESH = 'cap_refresh' 42CAP_ENHANCED_REFRESH = 'cap_enhanced_refresh' 43CAP_FOUR_OCTET_AS_NUMBER = 'cap_four_octet_as_number' 44CAP_MBGP_IPV4 = 'cap_mbgp_ipv4' 45CAP_MBGP_IPV6 = 'cap_mbgp_ipv6' 46CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4' 47CAP_MBGP_VPNV6 = 'cap_mbgp_vpnv6' 48CAP_MBGP_EVPN = 'cap_mbgp_evpn' 49CAP_MBGP_IPV4FS = 'cap_mbgp_ipv4fs' 50CAP_MBGP_IPV6FS = 'cap_mbgp_ipv6fs' 51CAP_MBGP_VPNV4FS = 'cap_mbgp_vpnv4fs' 52CAP_MBGP_VPNV6FS = 'cap_mbgp_vpnv6fs' 53CAP_MBGP_L2VPNFS = 'cap_mbgp_l2vpnfs' 54CAP_RTC = 'cap_rtc' 55RTC_AS = 'rtc_as' 56HOLD_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. 61MAX_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 66ADVERTISE_PEER_AS = 'advertise_peer_as' 67 68# MED - MULTI_EXIT_DISC 69MULTI_EXIT_DISC = 'multi_exit_disc' 70 71# Extended community attribute route origin. 72SITE_OF_ORIGINS = 'site_of_origins' 73 74# Constants related to errors. 75CONF_NAME = 'conf_name' 76CONF_VALUE = 'conf_value' 77 78# Max. value limits 79MAX_NUM_IMPORT_RT = 1000 80MAX_NUM_EXPORT_RT = 250 81MAX_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.') 90class 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.') 98class 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.') 114class 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.') 134class 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) 158class 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 265class 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 358class 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) 440class 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 451class 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 472class 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 494class 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) 546def 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) 555def 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) 564def 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) 572def 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) 580def 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) 592def 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) 600def 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) 608def 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) 616def 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) 625def 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) 634def 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) 643def 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) 652def 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) 660def 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) 669def 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) 678def 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) 687def 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) 696def 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) 705def 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) 714def 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) 722def 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) 732def 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) 740def 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) 758def 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) 769def 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 781def 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 794