blob: a13105211e908f9ef7760e1817acb21af7ec8885 [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):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080048 name = 'ponsim_onu'
49
50 supported_device_types = [
51 DeviceType(
52 id=name,
53 adapter=name,
54 accepts_bulk_flow_update=True
55 )
56 ]
57
58 def __init__(self, adapter_agent, config):
59 self.adapter_agent = adapter_agent
60 self.config = config
61 self.descriptor = Adapter(
62 id=self.name,
63 vendor='Voltha project',
64 version='0.4',
65 config=AdapterConfig(log_level=LogLevel.INFO)
66 )
67 self.devices_handlers = dict() # device_id -> PonSimOltHandler()
68
69 def start(self):
70 log.debug('starting')
71 log.info('started')
72
73 def stop(self):
74 log.debug('stopping')
75 log.info('stopped')
76
77 def adapter_descriptor(self):
78 return self.descriptor
79
80 def device_types(self):
81 return DeviceTypes(items=self.supported_device_types)
82
83 def health(self):
84 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
85
86 def change_master_state(self, master):
87 raise NotImplementedError()
88
Sergio Slobodrianec864c62017-03-09 11:41:43 -050089 def update_pm_config(self, device, pm_configs):
90 raise NotImplementedError()
91
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080092 def adopt_device(self, device):
93 self.devices_handlers[device.id] = PonSimOnuHandler(self, device.id)
94 reactor.callLater(0, self.devices_handlers[device.id].activate, device)
95 return device
96
97 def abandon_device(self, device):
98 raise NotImplementedError()
99
Khen Nursimulud068d812017-03-06 11:44:18 -0500100 def disable_device(self, device):
101 log.info('disable-device', device_id=device.id)
102 reactor.callLater(0, self.devices_handlers[device.id].disable)
103 return device
104
105 def reenable_device(self, device):
106 log.info('reenable-device', device_id=device.id)
107 reactor.callLater(0, self.devices_handlers[device.id].reenable)
108 return device
109
110 def reboot_device(self, device):
111 log.info('rebooting', device_id=device.id)
112 reactor.callLater(0, self.devices_handlers[device.id].reboot)
113 return device
114
115 def delete_device(self, device):
116 log.info('delete-device', device_id=device.id)
117 reactor.callLater(0, self.devices_handlers[device.id].delete)
118 return device
119
120 def get_device_details(self, device):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800121 raise NotImplementedError()
122
123 def update_flows_bulk(self, device, flows, groups):
124 log.info('bulk-flow-update', device_id=device.id,
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400125 flows=flows, groups=groups)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800126 assert len(groups.items) == 0
127 handler = self.devices_handlers[device.id]
128 return handler.update_flow_table(flows.items)
129
130 def update_flows_incrementally(self, device, flow_changes, group_changes):
131 raise NotImplementedError()
132
133 def send_proxied_message(self, proxy_address, msg):
134 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
135
136 def receive_proxied_message(self, proxy_address, msg):
137 log.info('receive-proxied-message', proxy_address=proxy_address,
138 device_id=proxy_address.device_id, msg=msg)
139 handler = self.devices_handlers[proxy_address.device_id]
140 handler.receive_message(msg)
141
142 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
143 log.info('packet-out', logical_device_id=logical_device_id,
144 egress_port_no=egress_port_no, msg_len=len(msg))
145
146
147class PonSimOnuHandler(object):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800148 def __init__(self, adapter, device_id):
149 self.adapter = adapter
150 self.adapter_agent = adapter.adapter_agent
151 self.device_id = device_id
152 self.log = structlog.get_logger(device_id=device_id)
153 self.incoming_messages = DeferredQueue()
154 self.proxy_address = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500155 # reference of uni_port is required when re-enabling the device if
156 # it was disabled previously
157 self.uni_port = None
158 self.pon_port = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800159
160 def receive_message(self, msg):
161 self.incoming_messages.put(msg)
162
163 def activate(self, device):
164 self.log.info('activating')
165
166 # first we verify that we got parent reference and proxy info
167 assert device.parent_id
168 assert device.proxy_address.device_id
169 assert device.proxy_address.channel_id
170
171 # register for proxied messages right away
172 self.proxy_address = device.proxy_address
173 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
174
175 # populate device info
176 device.root = True
177 device.vendor = 'ponsim'
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400178 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800179 device.connect_status = ConnectStatus.REACHABLE
180 self.adapter_agent.update_device(device)
181
182 # register physical ports
Khen Nursimulud068d812017-03-06 11:44:18 -0500183 self.uni_port = Port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800184 port_no=2,
185 label='UNI facing Ethernet port',
186 type=Port.ETHERNET_UNI,
187 admin_state=AdminState.ENABLED,
188 oper_status=OperStatus.ACTIVE
189 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500190 self.pon_port = Port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800191 port_no=1,
192 label='PON port',
193 type=Port.PON_ONU,
194 admin_state=AdminState.ENABLED,
195 oper_status=OperStatus.ACTIVE,
196 peers=[
197 Port.PeerPort(
198 device_id=device.parent_id,
199 port_no=device.parent_port_no
200 )
201 ]
Khen Nursimulud068d812017-03-06 11:44:18 -0500202 )
203 self.adapter_agent.add_port(device.id, self.uni_port)
204 self.adapter_agent.add_port(device.id, self.pon_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800205
206 # add uni port to logical device
207 parent_device = self.adapter_agent.get_device(device.parent_id)
208 logical_device_id = parent_device.parent_id
209 assert logical_device_id
210 port_no = device.proxy_address.channel_id
211 cap = OFPPF_1GB_FD | OFPPF_FIBER
212 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
213 id='uni-{}'.format(port_no),
214 ofp_port=ofp_port(
215 port_no=port_no,
216 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
217 name='uni-{}'.format(port_no),
218 config=0,
219 state=OFPPS_LIVE,
220 curr=cap,
221 advertised=cap,
222 peer=cap,
223 curr_speed=OFPPF_1GB_FD,
224 max_speed=OFPPF_1GB_FD
225 ),
226 device_id=device.id,
Khen Nursimulud068d812017-03-06 11:44:18 -0500227 device_port_no=self.uni_port.port_no
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800228 ))
229
230 device = self.adapter_agent.get_device(device.id)
231 device.oper_status = OperStatus.ACTIVE
232 self.adapter_agent.update_device(device)
233
234 @inlineCallbacks
235 def update_flow_table(self, flows):
236
237 # we need to proxy through the OLT to get to the ONU
238
239 # reset response queue
240 while self.incoming_messages.pending:
241 yield self.incoming_messages.get()
242
243 msg = FlowTable(
244 port=self.proxy_address.channel_id,
245 flows=flows
246 )
247 self.adapter_agent.send_proxied_message(self.proxy_address, msg)
248
249 yield self.incoming_messages.get()
Khen Nursimulud068d812017-03-06 11:44:18 -0500250
Khen Nursimulud068d812017-03-06 11:44:18 -0500251 @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
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400265 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500266 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
Khen Nursimulud068d812017-03-06 11:44:18 -0500278 def disable(self):
279 self.log.info('disabling', device_id=self.device_id)
280
281 # Get the latest device reference
282 device = self.adapter_agent.get_device(self.device_id)
283
284 # Disable all ports on that device
285 self.adapter_agent.disable_all_ports(self.device_id)
286
287 # Update the device operational status to UNKNOWN
288 device.oper_status = OperStatus.UNKNOWN
289 device.connect_status = ConnectStatus.UNREACHABLE
290 self.adapter_agent.update_device(device)
291
292 # Remove the uni logical port from the OLT, if still present
293 parent_device = self.adapter_agent.get_device(device.parent_id)
294 assert parent_device
295 logical_device_id = parent_device.parent_id
296 assert logical_device_id
297 port_no = device.proxy_address.channel_id
298 port_id = 'uni-{}'.format(port_no)
299 try:
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400300 port = self.adapter_agent.get_logical_port(logical_device_id,
301 port_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500302 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
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400316 self.adapter_agent.unregister_for_proxied_messages(
317 device.proxy_address)
Khen Nursimulud068d812017-03-06 11:44:18 -0500318
319 # TODO:
320 # 1) Remove all flows from the device
321 # 2) Remove the device from ponsim
322
323 self.log.info('disabled', device_id=device.id)
324
Khen Nursimulud068d812017-03-06 11:44:18 -0500325 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
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400342 self.adapter_agent.enable_all_ports(self.device_id)
343
344 # Add the pon port reference to the parent
345 self.adapter_agent.add_port_reference_to_parent(device.id,
346 self.pon_port)
Khen Nursimulud068d812017-03-06 11:44:18 -0500347
348 # Update the connect status to REACHABLE
349 device.connect_status = ConnectStatus.REACHABLE
350 self.adapter_agent.update_device(device)
351
352 # re-add uni port to logical device
353 parent_device = self.adapter_agent.get_device(device.parent_id)
354 logical_device_id = parent_device.parent_id
355 assert logical_device_id
356 port_no = device.proxy_address.channel_id
357 cap = OFPPF_1GB_FD | OFPPF_FIBER
358 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
359 id='uni-{}'.format(port_no),
360 ofp_port=ofp_port(
361 port_no=port_no,
362 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
363 name='uni-{}'.format(port_no),
364 config=0,
365 state=OFPPS_LIVE,
366 curr=cap,
367 advertised=cap,
368 peer=cap,
369 curr_speed=OFPPF_1GB_FD,
370 max_speed=OFPPF_1GB_FD
371 ),
372 device_id=device.id,
373 device_port_no=self.uni_port.port_no
374 ))
375
376 device = self.adapter_agent.get_device(device.id)
377 device.oper_status = OperStatus.ACTIVE
378 self.adapter_agent.update_device(device)
379
380 self.log.info('re-enabled', device_id=device.id)
381
Khen Nursimulud068d812017-03-06 11:44:18 -0500382 def delete(self):
383 self.log.info('deleting', device_id=self.device_id)
384
385 # A delete request may be received when an OLT is dsiabled
386
387 # TODO:
388 # 1) Remove all flows from the device
389 # 2) Remove the device from ponsim
390
391 self.log.info('deleted', device_id=self.device_id)