blob: 1d7f2794a89a539c811aa1129cf2dc8f8e979e8a [file] [log] [blame]
Zsolt Harasztied091602016-12-08 13:36:38 -08001#
2# Copyright 2016 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18Tibit ONU device adapter
19"""
20
Zsolt Haraszti348d1932016-12-10 01:10:07 -080021import json
22
23from uuid import uuid4
24
Zsolt Harasztied091602016-12-08 13:36:38 -080025import structlog
26from zope.interface import implementer
27
Zsolt Haraszti348d1932016-12-10 01:10:07 -080028from scapy.layers.inet import ICMP, IP
29from scapy.layers.l2 import Ether
30from twisted.internet.defer import DeferredQueue, inlineCallbacks
31from twisted.internet import reactor
32
33from voltha.core.logical_device_agent import mac_str_to_tuple
34
Zsolt Harasztied091602016-12-08 13:36:38 -080035from voltha.adapters.interface import IAdapterInterface
36from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
Zsolt Haraszti348d1932016-12-10 01:10:07 -080037from voltha.protos.device_pb2 import Port
Zsolt Harasztied091602016-12-08 13:36:38 -080038from voltha.protos.device_pb2 import DeviceType, DeviceTypes
39from voltha.protos.health_pb2 import HealthStatus
Zsolt Haraszti348d1932016-12-10 01:10:07 -080040from voltha.protos.common_pb2 import LogLevel, ConnectStatus
Zsolt Haraszti348d1932016-12-10 01:10:07 -080041from voltha.protos.common_pb2 import OperStatus, AdminState
42
43from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
44from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_10GB_FD, \
45 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
46 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
47
48from scapy.packet import Packet, bind_layers
49from scapy.fields import StrField
Zsolt Harasztied091602016-12-08 13:36:38 -080050
51log = structlog.get_logger()
52
Zsolt Haraszti348d1932016-12-10 01:10:07 -080053from EOAM_TLV import AddStaticMacAddress, DeleteStaticMacAddress
54from EOAM_TLV import ClearStaticMacTable
Nathan Knuth6e57f332016-12-22 15:49:20 -080055from EOAM_TLV import DeviceId
56from EOAM import EOAMPayload, CablelabsOUI
57from EOAM import DPoEOpcode_GetRequest, DPoEOpcode_SetRequest
Zsolt Harasztied091602016-12-08 13:36:38 -080058
59@implementer(IAdapterInterface)
60class TibitOnuAdapter(object):
61
62 name = 'tibit_onu'
63
64 supported_device_types = [
65 DeviceType(
66 id='tibit_onu',
67 adapter=name,
68 accepts_bulk_flow_update=True
69 )
70 ]
71
72 def __init__(self, adapter_agent, config):
73 self.adapter_agent = adapter_agent
74 self.config = config
75 self.descriptor = Adapter(
76 id=self.name,
77 vendor='Tibit Communications Inc.',
78 version='0.1',
79 config=AdapterConfig(log_level=LogLevel.INFO)
80 )
Zsolt Haraszti348d1932016-12-10 01:10:07 -080081 self.incoming_messages = DeferredQueue()
Zsolt Harasztied091602016-12-08 13:36:38 -080082
83 def start(self):
84 log.debug('starting')
85 log.info('started')
86
87 def stop(self):
88 log.debug('stopping')
89 log.info('stopped')
90
91 def adapter_descriptor(self):
92 return self.descriptor
93
94 def device_types(self):
95 return DeviceTypes(items=self.supported_device_types)
96
97 def health(self):
98 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
99
100 def change_master_state(self, master):
101 raise NotImplementedError()
102
103 def adopt_device(self, device):
104 log.info('adopt-device', device=device)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800105 reactor.callLater(0.1, self._onu_device_activation, device)
Zsolt Harasztied091602016-12-08 13:36:38 -0800106 return device
107
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800108 @inlineCallbacks
109 def _onu_device_activation(self, device):
110 # first we verify that we got parent reference and proxy info
111 assert device.parent_id
112 assert device.proxy_address.device_id
113 assert device.proxy_address.channel_id
114
115 # TODO: For now, pretend that we were able to contact the device and obtain
116 # additional information about it. Should add real message.
117 device.vendor = 'Tibit Communications, Inc.'
118 device.model = '10G GPON ONU'
119 device.hardware_version = 'fa161020'
120 device.firmware_version = '16.10.01'
121 device.software_version = '1.0'
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800122 device.serial_number = uuid4().hex
123 device.connect_status = ConnectStatus.REACHABLE
124 self.adapter_agent.update_device(device)
125
126 # then shortly after we create some ports for the device
127 uni_port = Port(
128 port_no=2,
129 label='UNI facing Ethernet port',
130 type=Port.ETHERNET_UNI,
131 admin_state=AdminState.ENABLED,
132 oper_status=OperStatus.ACTIVE
133 )
134 self.adapter_agent.add_port(device.id, uni_port)
135 self.adapter_agent.add_port(device.id, Port(
136 port_no=1,
137 label='PON port',
138 type=Port.PON_ONU,
139 admin_state=AdminState.ENABLED,
140 oper_status=OperStatus.ACTIVE,
141 peers=[
142 Port.PeerPort(
143 device_id=device.parent_id,
144 port_no=device.parent_port_no
145 )
146 ]
147 ))
148
149 # TODO adding vports to the logical device shall be done by agent?
150 # then we create the logical device port that corresponds to the UNI
151 # port of the device
152
153 # obtain logical device id
154 parent_device = self.adapter_agent.get_device(device.parent_id)
155 logical_device_id = parent_device.parent_id
156 assert logical_device_id
157
158 # we are going to use the proxy_address.channel_id as unique number
159 # and name for the virtual ports, as this is guaranteed to be unique
160 # in the context of the OLT port, so it is also unique in the context
161 # of the logical device
162 port_no = device.proxy_address.channel_id
163 cap = OFPPF_10GB_FD | OFPPF_FIBER
164 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
165 id=str(port_no),
166 ofp_port=ofp_port(
167 port_no=port_no,
168 hw_addr=mac_str_to_tuple(device.mac_address),
169 name='uni-{}'.format(port_no),
170 config=0,
171 state=OFPPS_LIVE,
172 curr=cap,
173 advertised=cap,
174 peer=cap,
175 curr_speed=OFPPF_10GB_FD,
176 max_speed=OFPPF_10GB_FD
177 ),
178 device_id=device.id,
179 device_port_no=uni_port.port_no
180 ))
181
182 # simulate a proxied message sending and receving a reply
183 reply = yield self._message_exchange(device)
184
185 # and finally update to "ACTIVE"
186 device = self.adapter_agent.get_device(device.id)
187 device.oper_status = OperStatus.ACTIVE
188 self.adapter_agent.update_device(device)
189
Zsolt Harasztied091602016-12-08 13:36:38 -0800190 def abandon_device(self, device):
191 raise NotImplementedError(0
192 )
193 def deactivate_device(self, device):
194 raise NotImplementedError()
195
196 def update_flows_bulk(self, device, flows, groups):
197 log.debug('bulk-flow-update', device_id=device.id,
198 flows=flows, groups=groups)
199
200 def update_flows_incrementally(self, device, flow_changes, group_changes):
201 raise NotImplementedError()
202
203 def send_proxied_message(self, proxy_address, msg):
204 raise NotImplementedError()
205
206 def receive_proxied_message(self, proxy_address, msg):
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800207 log.debug('receive-proxied-message',
Zsolt Harasztied091602016-12-08 13:36:38 -0800208 proxy_address=proxy_address, msg=msg)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800209 self.incoming_messages.put(msg)
210
211 @inlineCallbacks
212 def _message_exchange(self, device):
213
214 # register for receiving async messages
215 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
216
217 # reset incoming message queue
218 while self.incoming_messages.pending:
219 _ = yield self.incoming_messages.get()
220
221 # construct message
Nathan Knuth6e57f332016-12-22 15:49:20 -0800222 msg = EOAMPayload(body=CablelabsOUI() /
223 DPoEOpcode_GetRequest() /
224 DeviceId()
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800225 )
226
227 # send message
Nathan Knuth6e57f332016-12-22 15:49:20 -0800228 log.info('ONU-send-proxied-message')
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800229 self.adapter_agent.send_proxied_message(device.proxy_address, msg)
230
231 log.info('ONU-log incoming messages BEFORE')
232 # wait till we detect incoming message
233 yield self.incoming_messages.get()
234 log.info('ONU-log incoming messages AFTER')
235
236 # by returning we allow the device to be shown as active, which
237 # indirectly verified that message passing works