backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / rtconf / common.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  Runtime configuration that applies to all bgp sessions, i.e. global settings.
18 """
19 import logging
20 import numbers
21
22 from ryu.lib import ip
23
24 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
25 from ryu.services.protocols.bgp.utils.validation import is_valid_asn
26
27 from ryu.services.protocols.bgp import rtconf
28 from ryu.services.protocols.bgp.rtconf.base import BaseConf
29 from ryu.services.protocols.bgp.rtconf.base import BaseConfListener
30 from ryu.services.protocols.bgp.rtconf.base import compute_optional_conf
31 from ryu.services.protocols.bgp.rtconf.base import ConfigTypeError
32 from ryu.services.protocols.bgp.rtconf.base import ConfigValueError
33 from ryu.services.protocols.bgp.rtconf.base import MissingRequiredConf
34 from ryu.services.protocols.bgp.rtconf.base import validate
35
36 LOG = logging.getLogger('bgpspeaker.rtconf.common')
37
38
39 # Global configuration settings.
40 LOCAL_AS = 'local_as'
41 ROUTER_ID = 'router_id'
42 CLUSTER_ID = 'cluster_id'
43 LABEL_RANGE = 'label_range'
44 LABEL_RANGE_MAX = 'max'
45 LABEL_RANGE_MIN = 'min'
46 LOCAL_PREF = 'local_pref'
47
48 # Similar to Cisco command 'allowas-in'. Allows the local ASN in the path.
49 # Facilitates auto rd, auto rt import/export
50 # ("rd auto/route-target both auto") and simplified spine/leaf architectures,
51 # sharing an ASN between e.g. leafs.
52 ALLOW_LOCAL_AS_IN_COUNT = 'allow_local_as_in_count'
53
54 # Configuration that can be set at global level as well as per context
55 # (session/vrf) level
56 # Nested configuration override global or higher level configuration as they
57 # are more granular.
58 # TODO(apgw-dev) Nested configuration overriding higher level configuration is
59 # currently low priority
60
61 # Similar to Cisco command 'bgp refresh stalepath-time'. To cause the router to
62 # remove stale routes from the BGP table even if the router does not receive a
63 # Route-Refresh EOR message The bgp refresh stalepath-time command is not
64 # needed under normal circumstances.
65 # TODO(PH): Support this feature (currently low priority)
66 REFRESH_STALEPATH_TIME = 'refresh_stalepath_time'
67
68 # Similar to Cisco command 'bgp refresh max-eor-time'. The bgp refresh max-eor-
69 # time command is not needed under normal  circumstances. You might configure
70 # the bgp refresh max-eor-time command in the event of continuous route
71 # flapping, when the router is unable to generate a Route- Refresh EOR message,
72 # in which case a Route-Refresh EOR is generated after the timer expires.
73 # TODO(PH): Support this feature (currently low priority)
74 REFRESH_MAX_EOR_TIME = 'refresh_max_eor_time'
75
76 BGP_CONN_RETRY_TIME = 'bgp_conn_retry_time'
77 BGP_SERVER_HOSTS = 'bgp_server_hosts'
78 BGP_SERVER_PORT = 'bgp_server_port'
79 TCP_CONN_TIMEOUT = 'tcp_conn_timeout'
80 MAX_PATH_EXT_RTFILTER_ALL = 'maximum_paths_external_rtfilter_all'
81
82
83 # Valid default values of some settings.
84 DEFAULT_LABEL_RANGE = (100, 100000)
85 DEFAULT_REFRESH_STALEPATH_TIME = 0
86 DEFAULT_REFRESH_MAX_EOR_TIME = 0
87 DEFAULT_BGP_SERVER_HOSTS = ('0.0.0.0', '::')
88 DEFAULT_BGP_SERVER_PORT = 179
89 DEFAULT_TCP_CONN_TIMEOUT = 30
90 DEFAULT_BGP_CONN_RETRY_TIME = 30
91 DEFAULT_MED = 0
92 DEFAULT_MAX_PATH_EXT_RTFILTER_ALL = True
93 DEFAULT_LOCAL_PREF = 100
94
95
96 @validate(name=ALLOW_LOCAL_AS_IN_COUNT)
97 def validate_allow_local_as_in_count(count):
98     if not isinstance(count, numbers.Integral):
99         raise ConfigTypeError(desc=('Configuration value for %s has to be '
100                                     'integral type' % ALLOW_LOCAL_AS_IN_COUNT))
101     if count < 0:
102         raise ConfigValueError(desc='Invalid local AS count %s' % count)
103
104     return count
105
106
107 @validate(name=LOCAL_AS)
108 def validate_local_as(asn):
109     if asn is None:
110         raise MissingRequiredConf(conf_name=LOCAL_AS)
111
112     if not is_valid_asn(asn):
113         raise ConfigValueError(desc='Invalid local_as configuration value: %s'
114                                % asn)
115     return asn
116
117
118 @validate(name=ROUTER_ID)
119 def validate_router_id(router_id):
120     if not router_id:
121         raise MissingRequiredConf(conf_name=ROUTER_ID)
122
123     if not isinstance(router_id, str):
124         raise ConfigTypeError(conf_name=ROUTER_ID)
125     if not is_valid_ipv4(router_id):
126         raise ConfigValueError(desc='Invalid router id %s' % router_id)
127
128     return router_id
129
130
131 @validate(name=CLUSTER_ID)
132 def validate_router_id(cluster_id):
133     if not isinstance(cluster_id, str):
134         raise ConfigTypeError(conf_name=CLUSTER_ID)
135     if not is_valid_ipv4(cluster_id):
136         raise ConfigValueError(desc='Invalid cluster id %s' % cluster_id)
137
138     return cluster_id
139
140
141 @validate(name=REFRESH_STALEPATH_TIME)
142 def validate_refresh_stalepath_time(rst):
143     if not isinstance(rst, numbers.Integral):
144         raise ConfigTypeError(desc=('Configuration value for %s has to be '
145                                     'integral type' % REFRESH_STALEPATH_TIME))
146     if rst < 0:
147         raise ConfigValueError(desc='Invalid refresh stalepath time %s' % rst)
148
149     return rst
150
151
152 @validate(name=REFRESH_MAX_EOR_TIME)
153 def validate_refresh_max_eor_time(rmet):
154     if not isinstance(rmet, numbers.Integral):
155         raise ConfigTypeError(desc=('Configuration value for %s has to be of '
156                                     'integral type ' % REFRESH_MAX_EOR_TIME))
157     if rmet < 0:
158         raise ConfigValueError(desc='Invalid refresh stalepath time %s' % rmet)
159
160     return rmet
161
162
163 @validate(name=LABEL_RANGE)
164 def validate_label_range(label_range):
165     min_label, max_label = label_range
166     if (not min_label or
167             not max_label or
168             not isinstance(min_label, numbers.Integral) or
169             not isinstance(max_label, numbers.Integral) or min_label < 17 or
170             min_label >= max_label):
171         raise ConfigValueError(desc=('Invalid label_range configuration value:'
172                                      ' (%s).' % label_range))
173
174     return label_range
175
176
177 @validate(name=BGP_SERVER_HOSTS)
178 def validate_bgp_server_hosts(hosts):
179     for host in hosts:
180         if not ip.valid_ipv4(host) and not ip.valid_ipv6(host):
181             raise ConfigTypeError(desc=('Invalid bgp sever hosts '
182                                         'configuration value %s' % hosts))
183
184     return hosts
185
186
187 @validate(name=BGP_SERVER_PORT)
188 def validate_bgp_server_port(server_port):
189     if not isinstance(server_port, numbers.Integral):
190         raise ConfigTypeError(desc=('Invalid bgp sever port configuration '
191                                     'value %s' % server_port))
192     if server_port < 0 or server_port > 65535:
193         raise ConfigValueError(desc='Invalid server port %s' % server_port)
194
195     return server_port
196
197
198 @validate(name=TCP_CONN_TIMEOUT)
199 def validate_tcp_conn_timeout(tcp_conn_timeout):
200     # TODO(apgw-dev) made-up some valid values for this settings, check if we
201     # have a standard value in any routers
202     if not isinstance(tcp_conn_timeout, numbers.Integral):
203         raise ConfigTypeError(desc=('Invalid tcp connection timeout '
204                                     'configuration value %s' %
205                                     tcp_conn_timeout))
206
207     if tcp_conn_timeout < 10:
208         raise ConfigValueError(desc=('Invalid tcp connection timeout'
209                                      ' configuration value %s' %
210                                      tcp_conn_timeout))
211
212     return tcp_conn_timeout
213
214
215 @validate(name=BGP_CONN_RETRY_TIME)
216 def validate_bgp_conn_retry_time(bgp_conn_retry_time):
217     if not isinstance(bgp_conn_retry_time, numbers.Integral):
218         raise ConfigTypeError(desc=('Invalid bgp conn. retry time '
219                                     'configuration value %s' %
220                                     bgp_conn_retry_time))
221
222     if bgp_conn_retry_time < 10:
223         raise ConfigValueError(desc=('Invalid bgp connection retry time'
224                                      ' configuration value %s' %
225                                      bgp_conn_retry_time))
226     return bgp_conn_retry_time
227
228
229 @validate(name=MAX_PATH_EXT_RTFILTER_ALL)
230 def validate_max_path_ext_rtfilter_all(max_path_ext_rtfilter_all):
231     if not isinstance(max_path_ext_rtfilter_all, bool):
232         raise ConfigTypeError(desc=('Invalid max_path_ext_rtfilter_all'
233                                     ' configuration value %s' %
234                                     max_path_ext_rtfilter_all))
235     return max_path_ext_rtfilter_all
236
237
238 @validate(name=LOCAL_PREF)
239 def validate_local_pref(local_pref):
240     if not isinstance(local_pref, numbers.Integral):
241         raise ConfigTypeError(desc=('Invalid local_pref'
242                                     ' configuration value %s' %
243                                     local_pref))
244     return local_pref
245
246
247 class CommonConf(BaseConf):
248     """Encapsulates configurations applicable to all peer sessions.
249
250     Currently if any of these configurations change, it is assumed that current
251     active peer session will be bought down and restarted.
252     """
253     CONF_CHANGED_EVT = 1
254
255     VALID_EVT = frozenset([CONF_CHANGED_EVT])
256
257     REQUIRED_SETTINGS = frozenset([ROUTER_ID, LOCAL_AS])
258
259     OPTIONAL_SETTINGS = frozenset([REFRESH_STALEPATH_TIME,
260                                    REFRESH_MAX_EOR_TIME, LABEL_RANGE,
261                                    BGP_SERVER_HOSTS, BGP_SERVER_PORT,
262                                    TCP_CONN_TIMEOUT,
263                                    BGP_CONN_RETRY_TIME,
264                                    MAX_PATH_EXT_RTFILTER_ALL,
265                                    ALLOW_LOCAL_AS_IN_COUNT,
266                                    CLUSTER_ID,
267                                    LOCAL_PREF])
268
269     def __init__(self, **kwargs):
270         super(CommonConf, self).__init__(**kwargs)
271
272     def _init_opt_settings(self, **kwargs):
273         super(CommonConf, self)._init_opt_settings(**kwargs)
274         self._settings[ALLOW_LOCAL_AS_IN_COUNT] = compute_optional_conf(
275             ALLOW_LOCAL_AS_IN_COUNT, 0, **kwargs)
276         self._settings[LABEL_RANGE] = compute_optional_conf(
277             LABEL_RANGE, DEFAULT_LABEL_RANGE, **kwargs)
278         self._settings[REFRESH_STALEPATH_TIME] = compute_optional_conf(
279             REFRESH_STALEPATH_TIME, DEFAULT_REFRESH_STALEPATH_TIME, **kwargs)
280         self._settings[REFRESH_MAX_EOR_TIME] = compute_optional_conf(
281             REFRESH_MAX_EOR_TIME, DEFAULT_REFRESH_MAX_EOR_TIME, **kwargs)
282         self._settings[BGP_SERVER_HOSTS] = compute_optional_conf(
283             BGP_SERVER_HOSTS, DEFAULT_BGP_SERVER_HOSTS, **kwargs)
284         self._settings[BGP_SERVER_PORT] = compute_optional_conf(
285             BGP_SERVER_PORT, DEFAULT_BGP_SERVER_PORT, **kwargs)
286         self._settings[TCP_CONN_TIMEOUT] = compute_optional_conf(
287             TCP_CONN_TIMEOUT, DEFAULT_TCP_CONN_TIMEOUT, **kwargs)
288         self._settings[BGP_CONN_RETRY_TIME] = compute_optional_conf(
289             BGP_CONN_RETRY_TIME, DEFAULT_BGP_CONN_RETRY_TIME, **kwargs)
290         self._settings[MAX_PATH_EXT_RTFILTER_ALL] = compute_optional_conf(
291             MAX_PATH_EXT_RTFILTER_ALL, DEFAULT_MAX_PATH_EXT_RTFILTER_ALL,
292             **kwargs)
293         self._settings[CLUSTER_ID] = compute_optional_conf(
294             CLUSTER_ID, kwargs[ROUTER_ID], **kwargs)
295         self._settings[LOCAL_PREF] = compute_optional_conf(
296             LOCAL_PREF, DEFAULT_LOCAL_PREF, **kwargs)
297
298     # =========================================================================
299     # Required attributes
300     # =========================================================================
301
302     @property
303     def local_as(self):
304         return self._settings[LOCAL_AS]
305
306     @property
307     def router_id(self):
308         return self._settings[ROUTER_ID]
309
310     # =========================================================================
311     # Optional attributes with valid defaults.
312     # =========================================================================
313     @property
314     def cluster_id(self):
315         return self._settings[CLUSTER_ID]
316
317     @property
318     def allow_local_as_in_count(self):
319         return self._settings[ALLOW_LOCAL_AS_IN_COUNT]
320
321     @property
322     def bgp_conn_retry_time(self):
323         return self._settings[BGP_CONN_RETRY_TIME]
324
325     @property
326     def tcp_conn_timeout(self):
327         return self._settings[TCP_CONN_TIMEOUT]
328
329     @property
330     def refresh_stalepath_time(self):
331         return self._settings[REFRESH_STALEPATH_TIME]
332
333     @property
334     def refresh_max_eor_time(self):
335         return self._settings[REFRESH_MAX_EOR_TIME]
336
337     @property
338     def label_range(self):
339         return self._settings[LABEL_RANGE]
340
341     @property
342     def bgp_server_hosts(self):
343         return self._settings[BGP_SERVER_HOSTS]
344
345     @property
346     def bgp_server_port(self):
347         return self._settings[BGP_SERVER_PORT]
348
349     @property
350     def max_path_ext_rtfilter_all(self):
351         return self._settings[MAX_PATH_EXT_RTFILTER_ALL]
352
353     @property
354     def local_pref(self):
355         return self._settings[LOCAL_PREF]
356
357     @classmethod
358     def get_opt_settings(self):
359         self_confs = super(CommonConf, self).get_opt_settings()
360         self_confs.update(CommonConf.OPTIONAL_SETTINGS)
361         return self_confs
362
363     @classmethod
364     def get_req_settings(self):
365         self_confs = super(CommonConf, self).get_req_settings()
366         self_confs.update(CommonConf.REQUIRED_SETTINGS)
367         return self_confs
368
369     @classmethod
370     def get_valid_evts(self):
371         self_valid_evts = super(CommonConf, self).get_valid_evts()
372         self_valid_evts.update(CommonConf.VALID_EVT)
373         return self_valid_evts
374
375     def update(self, **kwargs):
376         """Updates global configuration settings with given values.
377
378         First checks if given configuration values differ from current values.
379         If any of the configuration values changed, generates a change event.
380         Currently we generate change event for any configuration change.
381         Note: This method is idempotent.
382         """
383         # Update inherited configurations
384         super(CommonConf, self).update(**kwargs)
385         conf_changed = False
386
387         # Validate given configurations and check if value changed
388         for conf_name, conf_value in kwargs.items():
389             rtconf.base.get_validator(conf_name)(conf_value)
390             item1 = self._settings.get(conf_name, None)
391             item2 = kwargs.get(conf_name, None)
392
393             if item1 != item2:
394                 conf_changed = True
395
396         # If any configuration changed, we update configuration value and
397         # notify listeners
398         if conf_changed:
399             for conf_name, conf_value in kwargs.items():
400                 # Since all new values are already validated, we can use them
401                 self._settings[conf_name] = conf_value
402
403             self._notify_listeners(CommonConf.CONF_CHANGED_EVT, self)
404
405
406 class CommonConfListener(BaseConfListener):
407     """Base listener for various changes to common configurations."""
408
409     def __init__(self, global_conf):
410         super(CommonConfListener, self).__init__(global_conf)
411         global_conf.add_listener(CommonConf.CONF_CHANGED_EVT,
412                                  self.on_update_common_conf)
413
414     def on_update_common_conf(self, evt):
415         raise NotImplementedError('This method should be overridden.')