blob: 7e35c7f0a61811af2d8b2d13ea717eeb63bd2072 [file] [log] [blame]
khenaidoob9203542018-09-17 22:56:37 -04001#
2# Copyright 2017 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"""
18Fully simulated OLT/ONU adapter.
19"""
20
21import sys
22import structlog
23from twisted.internet.defer import DeferredQueue, inlineCallbacks
24from adapters.common.utils.asleep import asleep
25
26from adapters.iadapter import OnuAdapter
27from adapters.protos import third_party
28from adapters.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
29from adapters.protos.device_pb2 import Port
30from adapters.protos.logical_device_pb2 import LogicalPort
31from adapters.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
32 OFPPF_1GB_FD
33from adapters.protos.openflow_13_pb2 import ofp_port
34from adapters.protos.ponsim_pb2 import FlowTable
35from adapters.protos.core_adapter_pb2 import PortCapability
36
37_ = third_party
38log = structlog.get_logger()
39
40
41def mac_str_to_tuple(mac):
42 return tuple(int(d, 16) for d in mac.split(':'))
43
44class PonSimOnuAdapter(OnuAdapter):
45 def __init__(self, adapter_agent, config):
46 # DeviceType of ONU should be same as VENDOR ID of ONU Serial Number as specified by standard
47 # requires for identifying correct adapter or ranged ONU
48 super(PonSimOnuAdapter, self).__init__(adapter_agent=adapter_agent,
49 config=config,
50 device_handler_class=PonSimOnuHandler,
51 name='ponsim_onu',
52 vendor='Voltha project',
53 version='0.4',
54 device_type='ponsim_onu',
55 vendor_id='PSMO',
56 accepts_bulk_flow_update=True,
57 accepts_add_remove_flow_updates=False)
58
59
60class PonSimOnuHandler(object):
61 def __init__(self, adapter, device_id):
62 self.adapter = adapter
63 self.adapter_agent = adapter.adapter_agent
64 self.device_id = device_id
65 self.log = structlog.get_logger(device_id=device_id)
66 self.incoming_messages = DeferredQueue()
67 self.proxy_address = None
68 # reference of uni_port is required when re-enabling the device if
69 # it was disabled previously
70 self.uni_port = None
71 self.pon_port = None
72
73 def receive_message(self, msg):
74 self.incoming_messages.put(msg)
75
76
77 @inlineCallbacks
78 def activate(self, device):
79 self.log.info('activating')
80
81 # TODO: Register for proxy address
82 # # first we verify that we got parent reference and proxy info
83 # assert device.parent_id
84 # assert device.proxy_address.device_id
85 # assert device.proxy_address.channel_id
86 #
87 # # register for proxied messages right away
88 # self.proxy_address = device.proxy_address
89 # self.adapter_agent.register_for_proxied_messages(device.proxy_address)
90
91 # populate device info
92 device.root = False
93 device.vendor = 'ponsim'
94 device.model = 'n/a'
95 device.connect_status = ConnectStatus.REACHABLE
96 yield self.adapter_agent.device_update(device)
97
98 # register physical ports
99 self.uni_port = Port(
100 port_no=2,
101 label='UNI facing Ethernet port',
102 type=Port.ETHERNET_UNI,
103 admin_state=AdminState.ENABLED,
104 oper_status=OperStatus.ACTIVE
105 )
106 self.pon_port = Port(
107 port_no=1,
108 label='PON port',
109 type=Port.PON_ONU,
110 admin_state=AdminState.ENABLED,
111 oper_status=OperStatus.ACTIVE,
112 peers=[
113 Port.PeerPort(
114 device_id=device.parent_id,
115 port_no=device.parent_port_no
116 )
117 ]
118 )
119 self.adapter_agent.port_created(device.id, self.uni_port)
120 self.adapter_agent.port_created(device.id, self.pon_port)
121
122 yield self.adapter_agent.device_state_update(device.id, oper_status=OperStatus.ACTIVE)
123
124
125 def get_ofp_port_info(self, device, port_no):
126 # Since the adapter created the device port then it has the reference of the port to
127 # return the capability. TODO: Do a lookup on the NNI port number and return the
128 # appropriate attributes
129 self.log.info('get_ofp_port_info', port_no=port_no, device_id=device.id)
130 # port_no = device.proxy_address.channel_id
131 cap = OFPPF_1GB_FD | OFPPF_FIBER
132 return PortCapability(
133 port = LogicalPort (
134 id='uni-{}'.format(port_no),
135 ofp_port=ofp_port(
136 port_no=port_no,
137 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
138 name='uni-{}'.format(port_no),
139 config=0,
140 state=OFPPS_LIVE,
141 curr=cap,
142 advertised=cap,
143 peer=cap,
144 curr_speed=OFPPF_1GB_FD,
145 max_speed=OFPPF_1GB_FD
146 )
147 )
148 )
149
150 def _get_uni_port(self):
151 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_UNI)
152 if ports:
153 # For now, we use on one uni port
154 return ports[0]
155
156 def _get_pon_port(self):
157 ports = self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
158 if ports:
159 # For now, we use on one uni port
160 return ports[0]
161
162 def reconcile(self, device):
163 self.log.info('reconciling-ONU-device-starts')
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 # Set the connection status to REACHABLE
175 device.connect_status = ConnectStatus.REACHABLE
176 self.adapter_agent.update_device(device)
177
178 # TODO: Verify that the uni, pon and logical ports exists
179
180 # Mark the device as REACHABLE and ACTIVE
181 device = self.adapter_agent.get_device(device.id)
182 device.connect_status = ConnectStatus.REACHABLE
183 device.oper_status = OperStatus.ACTIVE
184 self.adapter_agent.update_device(device)
185
186 self.log.info('reconciling-ONU-device-ends')
187
188 @inlineCallbacks
189 def update_flow_table(self, flows):
190
191 # we need to proxy through the OLT to get to the ONU
192
193 # reset response queue
194 while self.incoming_messages.pending:
195 yield self.incoming_messages.get()
196
197 msg = FlowTable(
198 port=self.proxy_address.channel_id,
199 flows=flows
200 )
201 self.adapter_agent.send_proxied_message(self.proxy_address, msg)
202
203 yield self.incoming_messages.get()
204
205 def remove_from_flow_table(self, flows):
206 self.log.debug('remove-from-flow-table', flows=flows)
207 # TODO: Update PONSIM code to accept incremental flow changes.
208 # Once completed, the accepts_add_remove_flow_updates for this
209 # device type can be set to True
210
211 def add_to_flow_table(self, flows):
212 self.log.debug('add-to-flow-table', flows=flows)
213 # TODO: Update PONSIM code to accept incremental flow changes
214 # Once completed, the accepts_add_remove_flow_updates for this
215 # device type can be set to True
216
217 @inlineCallbacks
218 def reboot(self):
219 self.log.info('rebooting', device_id=self.device_id)
220
221 # Update the operational status to ACTIVATING and connect status to
222 # UNREACHABLE
223 device = self.adapter_agent.get_device(self.device_id)
224 previous_oper_status = device.oper_status
225 previous_conn_status = device.connect_status
226 device.oper_status = OperStatus.ACTIVATING
227 device.connect_status = ConnectStatus.UNREACHABLE
228 self.adapter_agent.update_device(device)
229
230 # Sleep 10 secs, simulating a reboot
231 # TODO: send alert and clear alert after the reboot
232 yield asleep(10)
233
234 # Change the operational status back to its previous state. With a
235 # real OLT the operational state should be the state the device is
236 # after a reboot.
237 # Get the latest device reference
238 device = self.adapter_agent.get_device(self.device_id)
239 device.oper_status = previous_oper_status
240 device.connect_status = previous_conn_status
241 self.adapter_agent.update_device(device)
242 self.log.info('rebooted', device_id=self.device_id)
243
244 def self_test_device(self, device):
245 """
246 This is called to Self a device based on a NBI call.
247 :param device: A Voltha.Device object.
248 :return: Will return result of self test
249 """
250 log.info('self-test-device', device=device.id)
251 raise NotImplementedError()
252
253 def disable(self):
254 self.log.info('disabling', device_id=self.device_id)
255
256 # Get the latest device reference
257 device = self.adapter_agent.get_device(self.device_id)
258
259 # Disable all ports on that device
260 self.adapter_agent.disable_all_ports(self.device_id)
261
262 # Update the device operational status to UNKNOWN
263 device.oper_status = OperStatus.UNKNOWN
264 device.connect_status = ConnectStatus.UNREACHABLE
265 self.adapter_agent.update_device(device)
266
267 # Remove the uni logical port from the OLT, if still present
268 parent_device = self.adapter_agent.get_device(device.parent_id)
269 assert parent_device
270 logical_device_id = parent_device.parent_id
271 assert logical_device_id
272 port_no = device.proxy_address.channel_id
273 port_id = 'uni-{}'.format(port_no)
274 try:
275 port = self.adapter_agent.get_logical_port(logical_device_id,
276 port_id)
277 self.adapter_agent.delete_logical_port(logical_device_id, port)
278 except KeyError:
279 self.log.info('logical-port-not-found', device_id=self.device_id,
280 portid=port_id)
281
282 # Remove pon port from parent
283 self.pon_port = self._get_pon_port()
284 self.adapter_agent.delete_port_reference_from_parent(self.device_id,
285 self.pon_port)
286
287 # Just updating the port status may be an option as well
288 # port.ofp_port.config = OFPPC_NO_RECV
289 # yield self.adapter_agent.update_logical_port(logical_device_id,
290 # port)
291 # Unregister for proxied message
292 self.adapter_agent.unregister_for_proxied_messages(
293 device.proxy_address)
294
295 # TODO:
296 # 1) Remove all flows from the device
297 # 2) Remove the device from ponsim
298
299 self.log.info('disabled', device_id=device.id)
300
301 def reenable(self):
302 self.log.info('re-enabling', device_id=self.device_id)
303 try:
304 # Get the latest device reference
305 device = self.adapter_agent.get_device(self.device_id)
306
307 # First we verify that we got parent reference and proxy info
308 assert device.parent_id
309 assert device.proxy_address.device_id
310 assert device.proxy_address.channel_id
311
312 # Re-register for proxied messages right away
313 self.proxy_address = device.proxy_address
314 self.adapter_agent.register_for_proxied_messages(
315 device.proxy_address)
316
317 # Re-enable the ports on that device
318 self.adapter_agent.enable_all_ports(self.device_id)
319
320 # Refresh the port reference
321 self.uni_port = self._get_uni_port()
322 self.pon_port = self._get_pon_port()
323
324 # Add the pon port reference to the parent
325 self.adapter_agent.add_port_reference_to_parent(device.id,
326 self.pon_port)
327
328 # Update the connect status to REACHABLE
329 device.connect_status = ConnectStatus.REACHABLE
330 self.adapter_agent.update_device(device)
331
332 # re-add uni port to logical device
333 parent_device = self.adapter_agent.get_device(device.parent_id)
334 logical_device_id = parent_device.parent_id
335 assert logical_device_id
336 port_no = device.proxy_address.channel_id
337 cap = OFPPF_1GB_FD | OFPPF_FIBER
338 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
339 id='uni-{}'.format(port_no),
340 ofp_port=ofp_port(
341 port_no=port_no,
342 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
343 name='uni-{}'.format(port_no),
344 config=0,
345 state=OFPPS_LIVE,
346 curr=cap,
347 advertised=cap,
348 peer=cap,
349 curr_speed=OFPPF_1GB_FD,
350 max_speed=OFPPF_1GB_FD
351 ),
352 device_id=device.id,
353 device_port_no=self.uni_port.port_no
354 ))
355
356 device = self.adapter_agent.get_device(device.id)
357 device.oper_status = OperStatus.ACTIVE
358 self.adapter_agent.update_device(device)
359
360 self.log.info('re-enabled', device_id=device.id)
361 except Exception, e:
362 self.log.exception('error-reenabling', e=e)
363
364 def delete(self):
365 self.log.info('deleting', device_id=self.device_id)
366
367 # A delete request may be received when an OLT is dsiabled
368
369 # TODO:
370 # 1) Remove all flows from the device
371 # 2) Remove the device from ponsim
372
373 self.log.info('deleted', device_id=self.device_id)