blob: 36d001333ac9966f4bf9fa90eb1abf1516e270ba [file] [log] [blame]
Zsolt Haraszti656ecc62016-12-28 15:08:23 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -08003#
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"""
18Fully simulated OLT/ONU adapter.
19"""
20
21import structlog
22from twisted.internet import reactor
23from twisted.internet.defer import DeferredQueue, inlineCallbacks
24from zope.interface import implementer
Khen Nursimulud068d812017-03-06 11:44:18 -050025from common.utils.asleep import asleep
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080026
27from voltha.adapters.interface import IAdapterInterface
28from voltha.core.logical_device_agent import mac_str_to_tuple
29from voltha.protos import third_party
30from voltha.protos.adapter_pb2 import Adapter
31from voltha.protos.adapter_pb2 import AdapterConfig
32from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
33 AdminState
34from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port
35from voltha.protos.health_pb2 import HealthStatus
36from voltha.protos.logical_device_pb2 import LogicalPort
Khen Nursimulud068d812017-03-06 11:44:18 -050037from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
38 OFPPF_1GB_FD, OFPPC_NO_RECV
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080039from voltha.protos.openflow_13_pb2 import ofp_port
40from voltha.protos.ponsim_pb2 import FlowTable
41
42_ = third_party
43log = structlog.get_logger()
44
45
46@implementer(IAdapterInterface)
47class PonSimOnuAdapter(object):
48
49 name = 'ponsim_onu'
50
51 supported_device_types = [
52 DeviceType(
53 id=name,
54 adapter=name,
55 accepts_bulk_flow_update=True
56 )
57 ]
58
59 def __init__(self, adapter_agent, config):
60 self.adapter_agent = adapter_agent
61 self.config = config
62 self.descriptor = Adapter(
63 id=self.name,
64 vendor='Voltha project',
65 version='0.4',
66 config=AdapterConfig(log_level=LogLevel.INFO)
67 )
68 self.devices_handlers = dict() # device_id -> PonSimOltHandler()
69
70 def start(self):
71 log.debug('starting')
72 log.info('started')
73
74 def stop(self):
75 log.debug('stopping')
76 log.info('stopped')
77
78 def adapter_descriptor(self):
79 return self.descriptor
80
81 def device_types(self):
82 return DeviceTypes(items=self.supported_device_types)
83
84 def health(self):
85 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
86
87 def change_master_state(self, master):
88 raise NotImplementedError()
89
90 def adopt_device(self, device):
91 self.devices_handlers[device.id] = PonSimOnuHandler(self, device.id)
92 reactor.callLater(0, self.devices_handlers[device.id].activate, device)
93 return device
94
95 def abandon_device(self, device):
96 raise NotImplementedError()
97
Khen Nursimulud068d812017-03-06 11:44:18 -050098 def disable_device(self, device):
99 log.info('disable-device', device_id=device.id)
100 reactor.callLater(0, self.devices_handlers[device.id].disable)
101 return device
102
103 def reenable_device(self, device):
104 log.info('reenable-device', device_id=device.id)
105 reactor.callLater(0, self.devices_handlers[device.id].reenable)
106 return device
107
108 def reboot_device(self, device):
109 log.info('rebooting', device_id=device.id)
110 reactor.callLater(0, self.devices_handlers[device.id].reboot)
111 return device
112
113 def delete_device(self, device):
114 log.info('delete-device', device_id=device.id)
115 reactor.callLater(0, self.devices_handlers[device.id].delete)
116 return device
117
118 def get_device_details(self, device):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800119 raise NotImplementedError()
120
121 def update_flows_bulk(self, device, flows, groups):
122 log.info('bulk-flow-update', device_id=device.id,
123 flows=flows, groups=groups)
124 assert len(groups.items) == 0
125 handler = self.devices_handlers[device.id]
126 return handler.update_flow_table(flows.items)
127
128 def update_flows_incrementally(self, device, flow_changes, group_changes):
129 raise NotImplementedError()
130
131 def send_proxied_message(self, proxy_address, msg):
132 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
133
134 def receive_proxied_message(self, proxy_address, msg):
135 log.info('receive-proxied-message', proxy_address=proxy_address,
136 device_id=proxy_address.device_id, msg=msg)
137 handler = self.devices_handlers[proxy_address.device_id]
138 handler.receive_message(msg)
139
140 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
141 log.info('packet-out', logical_device_id=logical_device_id,
142 egress_port_no=egress_port_no, msg_len=len(msg))
143
144
145class PonSimOnuHandler(object):
146
147 def __init__(self, adapter, device_id):
148 self.adapter = adapter
149 self.adapter_agent = adapter.adapter_agent
150 self.device_id = device_id
151 self.log = structlog.get_logger(device_id=device_id)
152 self.incoming_messages = DeferredQueue()
153 self.proxy_address = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500154 # reference of uni_port is required when re-enabling the device if
155 # it was disabled previously
156 self.uni_port = None
157 self.pon_port = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800158
159 def receive_message(self, msg):
160 self.incoming_messages.put(msg)
161
162 def activate(self, device):
163 self.log.info('activating')
164
165 # first we verify that we got parent reference and proxy info
166 assert device.parent_id
167 assert device.proxy_address.device_id
168 assert device.proxy_address.channel_id
169
170 # register for proxied messages right away
171 self.proxy_address = device.proxy_address
172 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
173
174 # populate device info
175 device.root = True
176 device.vendor = 'ponsim'
177 device.model ='n/a'
178 device.connect_status = ConnectStatus.REACHABLE
179 self.adapter_agent.update_device(device)
180
181 # register physical ports
Khen Nursimulud068d812017-03-06 11:44:18 -0500182 self.uni_port = Port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800183 port_no=2,
184 label='UNI facing Ethernet port',
185 type=Port.ETHERNET_UNI,
186 admin_state=AdminState.ENABLED,
187 oper_status=OperStatus.ACTIVE
188 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500189 self.pon_port = Port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800190 port_no=1,
191 label='PON port',
192 type=Port.PON_ONU,
193 admin_state=AdminState.ENABLED,
194 oper_status=OperStatus.ACTIVE,
195 peers=[
196 Port.PeerPort(
197 device_id=device.parent_id,
198 port_no=device.parent_port_no
199 )
200 ]
Khen Nursimulud068d812017-03-06 11:44:18 -0500201 )
202 self.adapter_agent.add_port(device.id, self.uni_port)
203 self.adapter_agent.add_port(device.id, self.pon_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800204
205 # add uni port to logical device
206 parent_device = self.adapter_agent.get_device(device.parent_id)
207 logical_device_id = parent_device.parent_id
208 assert logical_device_id
209 port_no = device.proxy_address.channel_id
210 cap = OFPPF_1GB_FD | OFPPF_FIBER
211 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
212 id='uni-{}'.format(port_no),
213 ofp_port=ofp_port(
214 port_no=port_no,
215 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
216 name='uni-{}'.format(port_no),
217 config=0,
218 state=OFPPS_LIVE,
219 curr=cap,
220 advertised=cap,
221 peer=cap,
222 curr_speed=OFPPF_1GB_FD,
223 max_speed=OFPPF_1GB_FD
224 ),
225 device_id=device.id,
Khen Nursimulud068d812017-03-06 11:44:18 -0500226 device_port_no=self.uni_port.port_no
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800227 ))
228
229 device = self.adapter_agent.get_device(device.id)
230 device.oper_status = OperStatus.ACTIVE
231 self.adapter_agent.update_device(device)
232
233 @inlineCallbacks
234 def update_flow_table(self, flows):
235
236 # we need to proxy through the OLT to get to the ONU
237
238 # reset response queue
239 while self.incoming_messages.pending:
240 yield self.incoming_messages.get()
241
242 msg = FlowTable(
243 port=self.proxy_address.channel_id,
244 flows=flows
245 )
246 self.adapter_agent.send_proxied_message(self.proxy_address, msg)
247
248 yield self.incoming_messages.get()
Khen Nursimulud068d812017-03-06 11:44:18 -0500249
250
251 @inlineCallbacks
252 def reboot(self):
253 self.log.info('rebooting', device_id=self.device_id)
254
255 # Update the operational status to ACTIVATING and connect status to
256 # UNREACHABLE
257 device = self.adapter_agent.get_device(self.device_id)
258 previous_oper_status = device.oper_status
259 previous_conn_status = device.connect_status
260 device.oper_status = OperStatus.ACTIVATING
261 device.connect_status = ConnectStatus.UNREACHABLE
262 self.adapter_agent.update_device(device)
263
264 # Sleep 10 secs, simulating a reboot
265 #TODO: send alert and clear alert after the reboot
266 yield asleep(10)
267
268 # Change the operational status back to its previous state. With a
269 # real OLT the operational state should be the state the device is
270 # after a reboot.
271 # Get the latest device reference
272 device = self.adapter_agent.get_device(self.device_id)
273 device.oper_status = previous_oper_status
274 device.connect_status = previous_conn_status
275 self.adapter_agent.update_device(device)
276 self.log.info('rebooted', device_id=self.device_id)
277
278
279 def disable(self):
280 self.log.info('disabling', device_id=self.device_id)
281
282 # Get the latest device reference
283 device = self.adapter_agent.get_device(self.device_id)
284
285 # Disable all ports on that device
286 self.adapter_agent.disable_all_ports(self.device_id)
287
288 # Update the device operational status to UNKNOWN
289 device.oper_status = OperStatus.UNKNOWN
290 device.connect_status = ConnectStatus.UNREACHABLE
291 self.adapter_agent.update_device(device)
292
293 # Remove the uni logical port from the OLT, if still present
294 parent_device = self.adapter_agent.get_device(device.parent_id)
295 assert parent_device
296 logical_device_id = parent_device.parent_id
297 assert logical_device_id
298 port_no = device.proxy_address.channel_id
299 port_id = 'uni-{}'.format(port_no)
300 try:
301 port = self.adapter_agent.get_logical_port(logical_device_id, port_id)
302 self.adapter_agent.delete_logical_port(logical_device_id, port)
303 except KeyError:
304 self.log.info('logical-port-not-found', device_id=self.device_id,
305 portid=port_id)
306
307 # Remove pon port from parent
308 self.adapter_agent.delete_port_reference_from_parent(self.device_id,
309 self.pon_port)
310
311 # Just updating the port status may be an option as well
312 # port.ofp_port.config = OFPPC_NO_RECV
313 # yield self.adapter_agent.update_logical_port(logical_device_id,
314 # port)
315 # Unregister for proxied message
316 self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
317
318 # TODO:
319 # 1) Remove all flows from the device
320 # 2) Remove the device from ponsim
321
322 self.log.info('disabled', device_id=device.id)
323
324
325 def reenable(self):
326 self.log.info('re-enabling', device_id=self.device_id)
327
328 # Get the latest device reference
329 device = self.adapter_agent.get_device(self.device_id)
330
331 # First we verify that we got parent reference and proxy info
332 assert self.uni_port
333 assert device.parent_id
334 assert device.proxy_address.device_id
335 assert device.proxy_address.channel_id
336
337 # Re-register for proxied messages right away
338 self.proxy_address = device.proxy_address
339 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
340
341 # Re-enable the ports on that device
342 self.adapter_agent.reenable_all_ports(self.device_id)
343
344 # Update the connect status to REACHABLE
345 device.connect_status = ConnectStatus.REACHABLE
346 self.adapter_agent.update_device(device)
347
348 # re-add uni port to logical device
349 parent_device = self.adapter_agent.get_device(device.parent_id)
350 logical_device_id = parent_device.parent_id
351 assert logical_device_id
352 port_no = device.proxy_address.channel_id
353 cap = OFPPF_1GB_FD | OFPPF_FIBER
354 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
355 id='uni-{}'.format(port_no),
356 ofp_port=ofp_port(
357 port_no=port_no,
358 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
359 name='uni-{}'.format(port_no),
360 config=0,
361 state=OFPPS_LIVE,
362 curr=cap,
363 advertised=cap,
364 peer=cap,
365 curr_speed=OFPPF_1GB_FD,
366 max_speed=OFPPF_1GB_FD
367 ),
368 device_id=device.id,
369 device_port_no=self.uni_port.port_no
370 ))
371
372 device = self.adapter_agent.get_device(device.id)
373 device.oper_status = OperStatus.ACTIVE
374 self.adapter_agent.update_device(device)
375
376 self.log.info('re-enabled', device_id=device.id)
377
378
379 def delete(self):
380 self.log.info('deleting', device_id=self.device_id)
381
382 # A delete request may be received when an OLT is dsiabled
383
384 # TODO:
385 # 1) Remove all flows from the device
386 # 2) Remove the device from ponsim
387
388 self.log.info('deleted', device_id=self.device_id)