blob: 6b57295e8295d5a59508baf0ddb992e128df4384 [file] [log] [blame]
Chip Bolingab8863d2018-03-22 14:50:31 -05001# Copyright 2017-present Adtran, Inc.
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 implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import json
16import random
17from adtran_zmq import AdtranZmqClient
18from enum import IntEnum
19
Chip Boling3810e132018-10-12 13:03:47 -050020DEFAULT_PIO_TCP_PORT = 5555
Chip Bolingab8863d2018-03-22 14:50:31 -050021
22
23class PioClient(AdtranZmqClient):
24 """
25 Adtran ZeroMQ Client for packet in/out service
26 """
27 def __init__(self, ip_address, rx_callback, port):
28 super(PioClient, self).__init__(ip_address, rx_callback, port)
29 self._seq_number = random.randint(1, 2**32)
30
31 class UrlType(IntEnum):
32 PACKET_IN = 0 # Packet In
33 PACKET_OUT = 1 # Packet Out
34 EVCMAPS_REQUEST = 2 # EVC-MAPs request
35 EVCMAPS_RESPONSE = 3 # EVC-MAPs response
36 UNKNOWN = 4 # UNKNOWN URL
37
38 def get_url_type(self, packet):
39 url_type = PioClient.UrlType.UNKNOWN
40 message = json.loads(packet)
41 if 'url' in message:
42 if message['url'] == 'adtran-olt-of-control/packet-in':
43 url_type = PioClient.UrlType.PACKET_IN
44 elif message['url'] == 'adtran-olt-of-control/packet-out':
45 url_type = PioClient.UrlType.PACKET_OUT
46 elif message['url'] == 'adtran-olt-of-control/evc-map-response':
47 url_type = PioClient.UrlType.EVCMAPS_RESPONSE
48 elif message['url'] == 'adtran-olt-of-control/evc-map-request':
49 url_type = PioClient.UrlType.EVCMAPS_REQUEST
50 return url_type
51
52 def decode_packet(self, packet):
53 from scapy.layers.l2 import Ether
54 try:
55 message = json.loads(packet)
56 self.log.debug('message', message=message)
57
58 for field in ['url', 'evc-map-name', 'total-len', 'port-number', 'message-contents']:
59 assert field in message, "Missing field '{}' in received packet".format(field)
60
61 decoded = message['message-contents'].decode('base64')
62
63 assert len(decoded.encode('hex'))/2 == message['total-len'], \
64 'Decoded length ({}) != Message Encoded length ({})'.\
65 format(len(decoded.encode('hex')), message['total-len'])
66
67 return int(message['port-number']), message['evc-map-name'], Ether(decoded)
68
69 except Exception as e:
70 self.log.exception('decode', e=e)
71 raise
72
73 @property
74 def sequence_number(self):
75 if self._seq_number >= 2**32:
76 self._seq_number = 0
77 else:
78 self._seq_number += 1
79
80 return self._seq_number
81
82 def encode_packet(self, egress_port, packet, map_name='TODO', exception_type=''):
83 """
84 Encode a message for transmission as a Packet Out
85 :param egress_port: (int) egress physical port number
86 :param packet: (str) actual message
87 :param map_name: (str) EVC-MAP Name
88 :param exception_type: (str) Type of exception
89 """
90 return json.dumps({
91 'url': 'adtran-olt-of-control/packet-out',
92 'buffer-id': self.sequence_number,
93 'total-len': len(packet),
94 'evc-map-name': map_name,
95 'exception-type': exception_type,
96 'port-number': egress_port,
97 'message-contents': packet.encode('base64')
98 })
99
100 def query_request_packet(self):
101 """
102 Create query-request to get all installed exceptions
103 :return: Request string
104 """
105 return json.dumps({
106 'url': 'adtran-olt-of-control/evc-map-request'
107 })
108
109 def decode_query_response_packet(self, packet, map_name=None):
110 """
111 Create query-request to get all installed exceptions
112 :param map_name: (str) EVC-MAP Name (None=all)
113 :param packet: returned query response packet
114 :return: list of evcmaps and associated exceptions
115 """
116 from scapy.layers.l2 import Ether
117 message = json.loads(packet)
118 self.log.debug('message', message=message)
119
120 if 'url' in message and message['url'] == 'adtran-olt-of-control/evc-map-response':
121 maps=message['evc-map-list']
122 if maps is not None:
123 self.log.debug('evc-maps-query-response', maps=maps)
124 return maps
125 return []