backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / zebra / client / zclient.py
1 # Copyright (C) 2017 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 Zebra Client corresponding to 'zclient' structure.
18 """
19
20 import os
21 import socket
22 import struct
23
24 from ryu import cfg
25 from ryu.base.app_manager import RyuApp
26 from ryu.lib import hub
27 from ryu.lib import ip
28 from ryu.lib.packet import zebra
29 from ryu.lib.packet import safi as packet_safi
30 from ryu.services.protocols.zebra import event
31 from ryu.services.protocols.zebra.client import event as zclient_event
32
33
34 CONF = cfg.CONF['zapi']
35 GLOBAL_CONF = cfg.CONF
36
37
38 def create_connection(address):
39     """
40     Wrapper for socket.create_connection() function.
41
42     If *address* (a 2-tuple ``(host, port)``) contains a valid IPv4/v6
43     address, passes *address* to socket.create_connection().
44     If *host* is valid path to Unix Domain socket, tries to connect to
45     the server listening on the given socket.
46
47     :param address: IP address or path to Unix Domain socket.
48     :return: Socket instance.
49     """
50     host, _port = address
51
52     if ip.valid_ipv4(host) or ip.valid_ipv6(host):
53         return socket.create_connection(address)
54     elif os.path.exists(host):
55         sock = None
56         try:
57             sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
58             sock.connect(host)
59         except socket.error as e:
60             if sock is not None:
61                 sock.close()
62             raise e
63         return sock
64     else:
65         raise ValueError('Invalid IP address or Unix Socket: %s' % host)
66
67
68 def get_zebra_route_type_by_name(route_type='BGP'):
69     """
70     Returns the constant value for Zebra route type named "ZEBRA_ROUTE_*"
71     from its name.
72
73     See "ZEBRA_ROUTE_*" constants in "ryu.lib.packet.zebra" module.
74
75     :param route_type: Route type name (e.g., Kernel, BGP).
76     :return: Constant value for Zebra route type.
77     """
78     return getattr(zebra, "ZEBRA_ROUTE_%s" % route_type.upper())
79
80
81 class ZServer(object):
82     """
83     Zebra server class.
84     """
85
86     def __init__(self, client):
87         self.client = client
88         self.logger = client.logger
89         self.is_active = False
90         self.sock = None  # Client socket connecting to Zebra server
91         self.threads = []
92
93     def start(self):
94         self.is_active = True
95         try:
96             self.sock = create_connection(self.client.zserv_addr)
97         except socket.error as e:
98             self.logger.exception(
99                 'Cannot connect to Zebra server%s: %s',
100                 self.client.zserv_addr, e)
101             self.stop()
102             return None
103
104         self.sock.settimeout(GLOBAL_CONF.socket_timeout)
105
106         self.threads.append(hub.spawn(self._send_loop))
107         self.threads.append(hub.spawn(self._recv_loop))
108
109         # Send the following messages at starting connection.
110         # - ZEBRA_HELLO to register route_type
111         # - ZEBRA_ROUTER_ID_ADD to get router_id
112         # - ZEBRA_INTERFACE_ADD to get info for interfaces
113         self.client.send_msg(
114             zebra.ZebraMessage(
115                 version=self.client.zserv_ver,
116                 body=zebra.ZebraHello(
117                     route_type=self.client.route_type,
118                     instance=0)))
119         self.client.send_msg(
120             zebra.ZebraMessage(
121                 version=self.client.zserv_ver,
122                 body=zebra.ZebraRouterIDAdd()))
123         self.client.send_msg(
124             zebra.ZebraMessage(
125                 version=self.client.zserv_ver,
126                 body=zebra.ZebraInterfaceAdd()))
127
128         self.client.send_event_to_observers(
129             zclient_event.EventZServConnected(self))
130
131         hub.joinall(self.threads)
132
133         self.client.send_event_to_observers(
134             zclient_event.EventZServDisconnected(self))
135
136     def stop(self):
137         self.is_active = False
138
139     def _send_loop(self):
140         try:
141             while self.is_active:
142                 buf = self.client.send_q.get()
143                 self.sock.sendall(buf)
144         except socket.error as e:
145             self.logger.exception(
146                 'Error while sending message to Zebra server%s: %s',
147                 self.client.zserv_addr, e)
148
149         self.stop()
150
151     def _recv_loop(self):
152         buf = b''
153         min_len = recv_len = zebra.ZebraMessage.get_header_size(
154             self.client.zserv_ver)
155         try:
156             while self.is_active:
157                 try:
158                     recv_buf = self.sock.recv(recv_len)
159                 except socket.timeout:
160                     continue
161
162                 if len(recv_buf) == 0:
163                     break
164
165                 buf += recv_buf
166                 while len(buf) >= min_len:
167                     (length,) = struct.unpack_from('!H', buf)
168                     if (length - len(buf)) > 0:
169                         # Need to receive remaining data
170                         recv_len = length - len(buf)
171                         break
172
173                     msg, _, buf = zebra._ZebraMessageFromZebra.parser(buf)
174
175                     ev = event.message_to_event(self.client, msg)
176                     if ev:
177                         self.client.send_event_to_observers(ev)
178
179         except socket.error as e:
180             self.logger.exception(
181                 'Error while sending message to Zebra server%s: %s',
182                 self.client.zserv_addr, e)
183
184         self.stop()
185
186
187 class ZClient(RyuApp):
188     """
189     The base class for Zebra client application.
190     """
191     _EVENTS = event.ZEBRA_EVENTS + [
192         zclient_event.EventZServConnected,
193         zclient_event.EventZServDisconnected,
194     ]
195
196     def __init__(self, *args, **kwargs):
197         super(ZClient, self).__init__(*args, **kwargs)
198         self.zserv = None  # ZServer instance
199         self.zserv_addr = (CONF.server_host, CONF.server_port)
200         self.zserv_ver = CONF.server_version
201         self.send_q = hub.Queue(16)
202         self.route_type = get_zebra_route_type_by_name(
203             CONF.client_route_type)
204
205     def start(self):
206         super(ZClient, self).start()
207
208         return hub.spawn(self._service_loop)
209
210     def _service_loop(self):
211         while self.is_active:
212             self.zserv = ZServer(self)
213             self.zserv.start()
214
215             hub.sleep(CONF.retry_interval)
216
217         self.close()
218
219     def close(self):
220         self.is_active = False
221         self._send_event(self._event_stop, None)
222         self.zserv.stop()
223
224     def send_msg(self, msg):
225         """
226         Sends Zebra message.
227
228         :param msg: Instance of py:class: `ryu.lib.packet.zebra.ZebraMessage`.
229         :return: Serialized msg if succeeded, otherwise None.
230         """
231         if not self.is_active:
232             self.logger.debug(
233                 'Cannot send message: Already deactivated: msg=%s', msg)
234             return
235         elif not self.send_q:
236             self.logger.debug(
237                 'Cannot send message: Send queue does not exist: msg=%s', msg)
238             return
239         elif self.zserv_ver != msg.version:
240             self.logger.debug(
241                 'Zebra protocol version mismatch:'
242                 'server_version=%d, msg.version=%d',
243                 self.zserv_ver, msg.version)
244             msg.version = self.zserv_ver  # fixup
245
246         self.send_q.put(msg.serialize())
247
248     def _send_ip_route_impl(
249             self, prefix, nexthops=None,
250             safi=packet_safi.UNICAST, flags=zebra.ZEBRA_FLAG_INTERNAL,
251             distance=None, metric=None, mtu=None, tag=None,
252             is_withdraw=False):
253         if ip.valid_ipv4(prefix):
254             if is_withdraw:
255                 msg_cls = zebra.ZebraIPv4RouteDelete
256             else:
257                 msg_cls = zebra.ZebraIPv4RouteAdd
258         elif ip.valid_ipv6(prefix):
259             if is_withdraw:
260                 msg_cls = zebra.ZebraIPv6RouteDelete
261             else:
262                 msg_cls = zebra.ZebraIPv6RouteAdd
263         else:
264             raise ValueError('Invalid prefix: %s' % prefix)
265
266         nexthop_list = []
267         for nexthop in nexthops:
268             if ip.valid_ipv4(nexthop):
269                 nexthop_list.append(zebra.NextHopIPv4(addr=nexthop))
270             elif ip.valid_ipv6(nexthop):
271                 nexthop_list.append(zebra.NextHopIPv6(addr=nexthop))
272             else:
273                 raise ValueError('Invalid nexthop: %s' % nexthop)
274
275         msg = zebra.ZebraMessage(
276             version=self.zserv_ver,
277             body=msg_cls(
278                 route_type=self.route_type,
279                 flags=flags,
280                 message=0,
281                 safi=safi,
282                 prefix=prefix,
283                 nexthops=nexthop_list,
284                 distance=distance,
285                 metric=metric,
286                 mtu=mtu,
287                 tag=tag,
288                 instance=0))
289         self.send_msg(msg)
290
291         return msg
292
293     def send_ip_route_add(
294             self, prefix, nexthops=None,
295             safi=packet_safi.UNICAST, flags=zebra.ZEBRA_FLAG_INTERNAL,
296             distance=None, metric=None, mtu=None, tag=None):
297         """
298         Sends ZEBRA_IPV4/v6_ROUTE_ADD message to Zebra daemon.
299
300         :param prefix: IPv4/v6 Prefix to advertise.
301         :param nexthops: List of nexthop addresses.
302         :param safi: SAFI to advertise.
303         :param flags: Message flags to advertise. See "ZEBRA_FLAG_*".
304         :param distance: (Optional) Distance to advertise.
305         :param metric: (Optional) Metric to advertise.
306         :param mtu: (Optional) MTU size to advertise.
307         :param tag: (Optional) TAG information to advertise.
308         :return: Zebra message instance to be sent. None if failed.
309         """
310         try:
311             return self._send_ip_route_impl(
312                 prefix=prefix, nexthops=nexthops, safi=safi, flags=flags,
313                 distance=distance, metric=metric, mtu=mtu, tag=tag,
314                 is_withdraw=False)
315         except ValueError as e:
316             self.logger.exception(
317                 'Cannot send IP route add message: %s', e)
318             return None
319
320     def send_ip_route_delete(
321             self, prefix, nexthops=None,
322             safi=packet_safi.UNICAST, flags=zebra.ZEBRA_FLAG_INTERNAL,
323             distance=None, metric=None, mtu=None, tag=None):
324         """
325         Sends ZEBRA_IPV4/v6_ROUTE_DELETE message to Zebra daemon.
326
327         :param prefix: IPv4/v6 Prefix to advertise.
328         :param nexthops: List of nexthop addresses.
329         :param safi: SAFI to advertise.
330         :param flags: Message flags to advertise. See "ZEBRA_FLAG_*".
331         :param distance: (Optional) Distance to advertise.
332         :param metric: (Optional) Metric to advertise.
333         :param mtu: (Optional) MTU size to advertise.
334         :param tag: (Optional) TAG information to advertise.
335         :return: Zebra message instance to be sent. None if failed.
336         """
337         try:
338             return self._send_ip_route_impl(
339                 prefix=prefix, nexthops=nexthops, safi=safi, flags=flags,
340                 distance=distance, metric=metric, mtu=mtu, tag=tag,
341                 is_withdraw=True)
342         except ValueError as e:
343             self.logger.exception(
344                 'Cannot send IP route delete message: %s', e)
345             return None