backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / application.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 This module provides a convenient application for using Ryu BGPSpeaker and for
18 writing your BGP application.
19
20 It reads a configuration file which includes settings for neighbors, routes
21 and some others.
22 Please refer to ``ryu/services/protocols/bgp/bgp_sample_conf.py`` for the
23 sample configuration.
24
25 Usage Example::
26
27     $ ryu-manager ryu/services/protocols/bgp/application.py \\
28         --bgp-app-config-file ryu/services/protocols/bgp/bgp_sample_conf.py
29
30 SSH Console
31 ===========
32
33 You can also use the SSH console and see the RIB and do some operations from
34 this console.
35 The SSH port and username/password can be configured by the configuration file.
36 You can check the help by hitting '?' key in this interface.
37
38 Example::
39
40     $ ssh localhost -p 4990
41
42     Hello, this is Ryu BGP speaker (version 4.19).
43
44     bgpd> # Hit '?' key
45      clear - allows to reset BGP connections
46      help - show this help
47      quit - exit this session
48      set - set runtime settings
49      show - shows runtime state information
50     bgpd>
51     bgpd> show rib all
52     Status codes: * valid, > best
53     Origin codes: i - IGP, e - EGP, ? - incomplete
54          Network        Labels   Next Hop   Reason      Metric LocPrf Path
55      *>  10.10.1.0/24   None     0.0.0.0    Only Path                 i
56     bgpd>
57
58 Integration with Other Applications
59 ===================================
60
61 ``ryu.services.protocols.bgp.application.RyuBGPSpeaker`` will notifies the
62 following events to other Ryu applications.
63
64     - ``EventBestPathChanged``
65     - ``EventAdjRibInChanged``
66     - ``EventPeerDown``
67     - ``EventPeerUp``
68
69 To catch these events, specify ``@set_ev_cls()`` decorator to the event
70 handlers in the Ryu applications.
71
72 Example Application::
73
74     # my_bgp_app.py
75
76     from ryu.base import app_manager
77     from ryu.controller.handler import set_ev_cls
78     from ryu.services.protocols.bgp import application as bgp_application
79
80
81     class MyBGPApp(app_manager.RyuApp):
82         _CONTEXTS = {
83             'ryubgpspeaker': bgp_application.RyuBGPSpeaker,
84         }
85
86         def __init__(self, *args, **kwargs):
87             super(MyBGPApp, self).__init__(*args, **kwargs)
88
89             # Stores "ryu.services.protocols.bgp.application.RyuBGPSpeaker"
90             # instance in order to call the APIs of
91             # "ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker" via
92             # "self.app.speaker".
93             # Please note at this time, "BGPSpeaker" is NOT instantiated yet.
94             self.app = kwargs['ryubgpspeaker']
95
96         @set_ev_cls(bgp_application.EventBestPathChanged)
97         def _best_patch_changed_handler(self, ev):
98             self.logger.info(
99                 'Best path changed: is_withdraw=%s, path=%s',
100                 ev.is_withdraw, ev.path)
101
102 Usage Example::
103
104     $ ryu-manager my_bgp_app.py \\
105         --bgp-app-config-file ryu/services/protocols/bgp/bgp_sample_conf.py
106
107 .. note::
108
109     For the APIs for ``ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker``,
110     please refer to :doc:`../library_bgp_speaker_ref`.
111
112 API Reference
113 =============
114 """
115
116 import logging
117 import os
118
119 from ryu import cfg
120 from ryu.lib import hub
121 from ryu.utils import load_source
122 from ryu.base.app_manager import RyuApp
123 from ryu.controller.event import EventBase
124 from ryu.services.protocols.bgp.base import add_bgp_error_metadata
125 from ryu.services.protocols.bgp.base import BGPSException
126 from ryu.services.protocols.bgp.base import BIN_ERROR
127 from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker
128 from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER
129 from ryu.services.protocols.bgp.net_ctrl import NC_RPC_BIND_IP
130 from ryu.services.protocols.bgp.net_ctrl import NC_RPC_BIND_PORT
131 from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
132 from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
133 from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
134 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
135 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6
136
137
138 LOG = logging.getLogger('bgpspeaker.application')
139
140 CONF = cfg.CONF['bgp-app']
141
142
143 @add_bgp_error_metadata(code=BIN_ERROR,
144                         sub_code=1,
145                         def_desc='Unknown bootstrap exception.')
146 class ApplicationException(BGPSException):
147     """
148     Specific Base exception related to `BSPSpeaker`.
149     """
150     pass
151
152
153 def validate_rpc_host(ip):
154     """
155     Validates the given ip for use as RPC server address.
156     """
157     if not is_valid_ipv4(ip) and not is_valid_ipv6(ip):
158         raise ApplicationException(
159             desc='Invalid RPC ip address: %s' % ip)
160     return ip
161
162
163 def load_config(config_file):
164     """
165     Validates the given file for use as the settings file for BGPSpeaker
166     and loads the configuration from the given file as a module instance.
167     """
168     if not config_file or not os.path.isfile(config_file):
169         raise ApplicationException(
170             desc='Invalid configuration file: %s' % config_file)
171
172     # Loads the configuration from the given file, if available.
173     try:
174         return load_source('bgpspeaker.application.settings', config_file)
175     except Exception as e:
176         raise ApplicationException(desc=str(e))
177
178
179 class EventBestPathChanged(EventBase):
180     """
181     Event called when any best remote path is changed due to UPDATE messages
182     or remote peer's down.
183
184     This event is the wrapper for ``best_path_change_handler`` of
185     ``bgpspeaker.BGPSpeaker``.
186
187     ``path`` attribute contains an instance of ``info_base.base.Path``
188     subclasses.
189
190     If ``is_withdraw`` attribute is ``True``, ``path`` attribute has the
191     information of the withdraw route.
192     """
193
194     def __init__(self, path, is_withdraw):
195         super(EventBestPathChanged, self).__init__()
196         self.path = path
197         self.is_withdraw = is_withdraw
198
199
200 class EventAdjRibInChanged(EventBase):
201     """
202     Event called when any adj-RIB-in path is changed due to UPDATE messages
203     or remote peer's down.
204
205     This event is the wrapper for ``adj_rib_in_change_handler`` of
206     ``bgpspeaker.BGPSpeaker``.
207
208     ``path`` attribute contains an instance of ``info_base.base.Path``
209     subclasses.
210
211     If ``is_withdraw`` attribute is ``True``, ``path`` attribute has the
212     information of the withdraw route.
213
214     ``peer_ip`` is the peer's IP address who sent this path.
215
216     ``peer_as`` is the peer's AS number who sent this path.
217     """
218
219     def __init__(self, path, is_withdraw, peer_ip, peer_as):
220         super(EventAdjRibInChanged, self).__init__()
221         self.path = path
222         self.is_withdraw = is_withdraw
223         self.peer_ip = peer_ip
224         self.peer_as = peer_as
225
226
227 class EventPeerDown(EventBase):
228     """
229     Event called when the session to the remote peer goes down.
230
231     This event is the wrapper for ``peer_down_handler`` of
232     ``bgpspeaker.BGPSpeaker``.
233
234     ``remote_ip`` attribute is the IP address of the remote peer.
235
236     ``remote_as`` attribute is the AS number of the remote peer.
237     """
238
239     def __init__(self, remote_ip, remote_as):
240         super(EventPeerDown, self).__init__()
241         self.remote_ip = remote_ip
242         self.remote_as = remote_as
243
244
245 class EventPeerUp(EventBase):
246     """
247     Event called when the session to the remote peer goes up.
248
249     This event is the wrapper for ``peer_up_handler`` of
250     ``bgpspeaker.BGPSpeaker``.
251
252     ``remote_ip`` attribute is the IP address of the remote peer.
253
254     ``remote_as`` attribute is the AS number of the remote peer.
255     """
256
257     def __init__(self, remote_ip, remote_as):
258         super(EventPeerUp, self).__init__()
259         self.remote_ip = remote_ip
260         self.remote_as = remote_as
261
262
263 class RyuBGPSpeaker(RyuApp):
264     """
265     Base application for implementing BGP applications.
266     """
267     _EVENTS = [
268         EventBestPathChanged,
269         EventAdjRibInChanged,
270         EventPeerDown,
271         EventPeerUp,
272     ]
273
274     def __init__(self, *args, **kwargs):
275         super(RyuBGPSpeaker, self).__init__(*args, **kwargs)
276         self.config_file = CONF.config_file
277
278         # BGPSpeaker instance (not instantiated yet)
279         self.speaker = None
280
281     def start(self):
282         super(RyuBGPSpeaker, self).start()
283
284         # If configuration file was provided and loaded successfully, we start
285         # BGPSpeaker using the given settings.
286         # If no configuration file is provided or if any minimum required
287         # setting is missing, BGPSpeaker will not be started.
288         if self.config_file:
289             LOG.debug('Loading config file %s...', self.config_file)
290             settings = load_config(self.config_file)
291
292             # Configure logging settings, if available.
293             if hasattr(settings, 'LOGGING'):
294                 # Not implemented yet.
295                 LOG.debug('Loading LOGGING settings... (NOT implemented yet)')
296                 # from logging.config import dictConfig
297                 # logging_settings = dictConfig(settings.LOGGING)
298
299             # Configure BGP settings, if available.
300             if hasattr(settings, 'BGP'):
301                 LOG.debug('Loading BGP settings...')
302                 self._start_speaker(settings.BGP)
303
304             # Configure SSH settings, if available.
305             if hasattr(settings, 'SSH'):
306                 LOG.debug('Loading SSH settings...')
307                 # Note: paramiko used in bgp.operator.ssh is the optional
308                 # requirements, imports bgp.operator.ssh here.
309                 from ryu.services.protocols.bgp.operator import ssh
310                 hub.spawn(ssh.SSH_CLI_CONTROLLER.start, **settings.SSH)
311
312         # Start RPC server with the given RPC settings.
313         rpc_settings = {
314             NC_RPC_BIND_PORT: CONF.rpc_port,
315             NC_RPC_BIND_IP: validate_rpc_host(CONF.rpc_host),
316         }
317         return hub.spawn(NET_CONTROLLER.start, **rpc_settings)
318
319     def _start_speaker(self, settings):
320         """
321         Starts BGPSpeaker using the given settings.
322         """
323         # Check required settings.
324         _required_settings = (
325             LOCAL_AS,
326             ROUTER_ID,
327         )
328         for required in _required_settings:
329             if required not in settings:
330                 raise ApplicationException(
331                     desc='Required BGP configuration missing: %s' % required)
332
333         # Set event notify handlers if no corresponding handler specified.
334         settings.setdefault(
335             'best_path_change_handler', self._notify_best_path_changed_event)
336         settings.setdefault(
337             'adj_rib_in_change_handler', self._notify_adj_rib_in_changed_event)
338         settings.setdefault(
339             'peer_down_handler', self._notify_peer_down_event)
340         settings.setdefault(
341             'peer_up_handler', self._notify_peer_up_event)
342
343         # Pop settings other than creating BGPSpeaker instance.
344         neighbors_settings = settings.pop('neighbors', [])
345         vrfs_settings = settings.pop('vrfs', [])
346         routes_settings = settings.pop('routes', [])
347
348         # Create BGPSpeaker instance.
349         LOG.debug('Starting BGPSpeaker...')
350         settings.setdefault('as_number', settings.pop(LOCAL_AS))
351         self.speaker = BGPSpeaker(**settings)
352
353         # Add neighbors.
354         LOG.debug('Adding neighbors...')
355         self._add_neighbors(neighbors_settings)
356
357         # Add VRFs.
358         LOG.debug('Adding VRFs...')
359         self._add_vrfs(vrfs_settings)
360
361         # Add routes
362         LOG.debug('Adding routes...')
363         self._add_routes(routes_settings)
364
365     def _notify_best_path_changed_event(self, ev):
366         ev = EventBestPathChanged(ev.path, ev.is_withdraw)
367         self.send_event_to_observers(ev)
368
369     def _notify_adj_rib_in_changed_event(self, ev, peer_ip, peer_as):
370         ev = EventAdjRibInChanged(ev.path, ev.is_withdraw, peer_ip, peer_as)
371         self.send_event_to_observers(ev)
372
373     def _notify_peer_down_event(self, remote_ip, remote_as):
374         ev = EventPeerDown(remote_ip, remote_as)
375         self.send_event_to_observers(ev)
376
377     def _notify_peer_up_event(self, remote_ip, remote_as):
378         ev = EventPeerUp(remote_ip, remote_as)
379         self.send_event_to_observers(ev)
380
381     def _add_neighbors(self, settings):
382         """
383         Add BGP neighbors from the given settings.
384
385         All valid neighbors are loaded.
386         Miss-configured neighbors are ignored and errors are logged.
387         """
388         for neighbor_settings in settings:
389             LOG.debug('Adding neighbor settings: %s', neighbor_settings)
390             try:
391                 self.speaker.neighbor_add(**neighbor_settings)
392             except RuntimeConfigError as e:
393                 LOG.exception(e)
394
395     def _add_vrfs(self, settings):
396         """
397         Add BGP VRFs from the given settings.
398
399         All valid VRFs are loaded.
400         Miss-configured VRFs are ignored and errors are logged.
401         """
402         for vrf_settings in settings:
403             LOG.debug('Adding VRF settings: %s', vrf_settings)
404             try:
405                 self.speaker.vrf_add(**vrf_settings)
406             except RuntimeConfigError as e:
407                 LOG.exception(e)
408
409     def _add_routes(self, settings):
410         """
411         Add BGP routes from given settings.
412
413         All valid routes are loaded.
414         Miss-configured routes are ignored and errors are logged.
415         """
416         for route_settings in settings:
417             if 'prefix' in route_settings:
418                 prefix_add = self.speaker.prefix_add
419             elif 'route_type' in route_settings:
420                 prefix_add = self.speaker.evpn_prefix_add
421             elif 'flowspec_family' in route_settings:
422                 prefix_add = self.speaker.flowspec_prefix_add
423             else:
424                 LOG.debug('Skip invalid route settings: %s', route_settings)
425                 continue
426
427             LOG.debug('Adding route settings: %s', route_settings)
428             try:
429                 prefix_add(**route_settings)
430             except RuntimeConfigError as e:
431                 LOG.exception(e)