blob: d1b2e277d8fa37dcd61b2a6bb75bdcb7d602e741 [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
khenaidoo92e62c52018-10-03 14:02:54 -040023from twisted.internet.defer import DeferredQueue, inlineCallbacks, returnValue
khenaidoob9203542018-09-17 22:56:37 -040024from 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'
khenaidoo92e62c52018-10-03 14:02:54 -040095 # device.connect_status = ConnectStatus.REACHABLE
khenaidoob9203542018-09-17 22:56:37 -040096 yield self.adapter_agent.device_update(device)
97
khenaidoo92e62c52018-10-03 14:02:54 -040098 # register physical ports
khenaidoob9203542018-09-17 22:56:37 -040099 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
khenaidoo92e62c52018-10-03 14:02:54 -0400122 yield self.adapter_agent.device_state_update(device.id, connect_status=ConnectStatus.REACHABLE, oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400123
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
khenaidoo92e62c52018-10-03 14:02:54 -0400150 # 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]
khenaidoob9203542018-09-17 22:56:37 -0400155
khenaidoo92e62c52018-10-03 14:02:54 -0400156 @inlineCallbacks
157 def _get_uni_port(self):
158 ports = yield self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_UNI)
159 returnValue(ports)
160
161 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400162 def _get_pon_port(self):
khenaidoo92e62c52018-10-03 14:02:54 -0400163 ports = yield self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
164 returnValue(ports)
165
166 # def _get_pon_port(self):
167 # ports = self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
168 # if ports:
169 # # For now, we use on one uni port
170 # return ports[0]
khenaidoob9203542018-09-17 22:56:37 -0400171
172 def reconcile(self, device):
173 self.log.info('reconciling-ONU-device-starts')
174
175 # first we verify that we got parent reference and proxy info
176 assert device.parent_id
177 assert device.proxy_address.device_id
178 assert device.proxy_address.channel_id
179
180 # register for proxied messages right away
181 self.proxy_address = device.proxy_address
182 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
183
184 # Set the connection status to REACHABLE
185 device.connect_status = ConnectStatus.REACHABLE
186 self.adapter_agent.update_device(device)
187
188 # TODO: Verify that the uni, pon and logical ports exists
189
190 # Mark the device as REACHABLE and ACTIVE
191 device = self.adapter_agent.get_device(device.id)
192 device.connect_status = ConnectStatus.REACHABLE
193 device.oper_status = OperStatus.ACTIVE
194 self.adapter_agent.update_device(device)
195
196 self.log.info('reconciling-ONU-device-ends')
197
198 @inlineCallbacks
199 def update_flow_table(self, flows):
200
201 # we need to proxy through the OLT to get to the ONU
202
203 # reset response queue
204 while self.incoming_messages.pending:
205 yield self.incoming_messages.get()
206
207 msg = FlowTable(
208 port=self.proxy_address.channel_id,
209 flows=flows
210 )
211 self.adapter_agent.send_proxied_message(self.proxy_address, msg)
212
213 yield self.incoming_messages.get()
214
215 def remove_from_flow_table(self, flows):
216 self.log.debug('remove-from-flow-table', flows=flows)
217 # TODO: Update PONSIM code to accept incremental flow changes.
218 # Once completed, the accepts_add_remove_flow_updates for this
219 # device type can be set to True
220
221 def add_to_flow_table(self, flows):
222 self.log.debug('add-to-flow-table', flows=flows)
223 # TODO: Update PONSIM code to accept incremental flow changes
224 # Once completed, the accepts_add_remove_flow_updates for this
225 # device type can be set to True
226
227 @inlineCallbacks
228 def reboot(self):
229 self.log.info('rebooting', device_id=self.device_id)
230
231 # Update the operational status to ACTIVATING and connect status to
232 # UNREACHABLE
233 device = self.adapter_agent.get_device(self.device_id)
234 previous_oper_status = device.oper_status
235 previous_conn_status = device.connect_status
236 device.oper_status = OperStatus.ACTIVATING
237 device.connect_status = ConnectStatus.UNREACHABLE
238 self.adapter_agent.update_device(device)
239
240 # Sleep 10 secs, simulating a reboot
241 # TODO: send alert and clear alert after the reboot
242 yield asleep(10)
243
244 # Change the operational status back to its previous state. With a
245 # real OLT the operational state should be the state the device is
246 # after a reboot.
247 # Get the latest device reference
248 device = self.adapter_agent.get_device(self.device_id)
249 device.oper_status = previous_oper_status
250 device.connect_status = previous_conn_status
251 self.adapter_agent.update_device(device)
252 self.log.info('rebooted', device_id=self.device_id)
253
254 def self_test_device(self, device):
255 """
256 This is called to Self a device based on a NBI call.
257 :param device: A Voltha.Device object.
258 :return: Will return result of self test
259 """
260 log.info('self-test-device', device=device.id)
261 raise NotImplementedError()
262
khenaidoo92e62c52018-10-03 14:02:54 -0400263
264 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400265 def disable(self):
266 self.log.info('disabling', device_id=self.device_id)
267
khenaidoob9203542018-09-17 22:56:37 -0400268 # Update the device operational status to UNKNOWN
khenaidoo92e62c52018-10-03 14:02:54 -0400269 yield self.adapter_agent.device_state_update(self.device_id, oper_status=OperStatus.UNKNOWN, connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400270
271 # TODO:
272 # 1) Remove all flows from the device
273 # 2) Remove the device from ponsim
274
khenaidoo92e62c52018-10-03 14:02:54 -0400275 self.log.info('disabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400276
khenaidoo92e62c52018-10-03 14:02:54 -0400277 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400278 def reenable(self):
279 self.log.info('re-enabling', device_id=self.device_id)
280 try:
281 # Get the latest device reference
khenaidoo92e62c52018-10-03 14:02:54 -0400282 # device = self.adapter_agent.get_device(self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400283
284 # First we verify that we got parent reference and proxy info
khenaidoo92e62c52018-10-03 14:02:54 -0400285 # assert device.parent_id
286 # assert device.proxy_address.device_id
287 # assert device.proxy_address.channel_id
khenaidoob9203542018-09-17 22:56:37 -0400288
289 # Re-register for proxied messages right away
khenaidoo92e62c52018-10-03 14:02:54 -0400290 # self.proxy_address = device.proxy_address
291 # self.adapter_agent.register_for_proxied_messages(
292 # device.proxy_address)
khenaidoob9203542018-09-17 22:56:37 -0400293
khenaidoo92e62c52018-10-03 14:02:54 -0400294 # Refresh the port reference - we only use one port for now
295 ports = yield self._get_uni_port()
296 self.log.info('re-enabling-uni-ports', ports=ports)
297 if ports.items:
298 self.uni_port = ports.items[0]
khenaidoob9203542018-09-17 22:56:37 -0400299
khenaidoo92e62c52018-10-03 14:02:54 -0400300 ports = yield self._get_pon_port()
301 self.log.info('re-enabling-pon-ports', ports=ports)
302 if ports.items:
303 self.pon_port = ports.items[0]
304
305 # Update the state of the UNI port
306 yield self.adapter_agent.port_state_update(self.device_id,
307 port_type=Port.ETHERNET_UNI,
308 port_no=self.uni_port.port_no,
309 oper_status=OperStatus.ACTIVE)
310
311 # Update the state of the PON port
312 yield self.adapter_agent.port_state_update(self.device_id,
313 port_type=Port.PON_ONU,
314 port_no=self.pon_port.port_no,
315 oper_status=OperStatus.ACTIVE)
316
317
318 # # Re-enable the ports on that device
319 # self.adapter_agent.enable_all_ports(self.device_id)
320
khenaidoob9203542018-09-17 22:56:37 -0400321
322 # Add the pon port reference to the parent
khenaidoo92e62c52018-10-03 14:02:54 -0400323 # self.adapter_agent.add_port_reference_to_parent(device.id,
324 # self.pon_port)
khenaidoob9203542018-09-17 22:56:37 -0400325
326 # Update the connect status to REACHABLE
khenaidoo92e62c52018-10-03 14:02:54 -0400327 # device.connect_status = ConnectStatus.REACHABLE
328 # self.adapter_agent.update_device(device)
khenaidoob9203542018-09-17 22:56:37 -0400329
330 # re-add uni port to logical device
khenaidoo92e62c52018-10-03 14:02:54 -0400331 # parent_device = self.adapter_agent.get_device(device.parent_id)
332 # logical_device_id = parent_device.parent_id
333 # assert logical_device_id
334 # port_no = device.proxy_address.channel_id
335 # cap = OFPPF_1GB_FD | OFPPF_FIBER
336 # self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
337 # id='uni-{}'.format(port_no),
338 # ofp_port=ofp_port(
339 # port_no=port_no,
340 # hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
341 # name='uni-{}'.format(port_no),
342 # config=0,
343 # state=OFPPS_LIVE,
344 # curr=cap,
345 # advertised=cap,
346 # peer=cap,
347 # curr_speed=OFPPF_1GB_FD,
348 # max_speed=OFPPF_1GB_FD
349 # ),
350 # device_id=device.id,
351 # device_port_no=self.uni_port.port_no
352 # ))
khenaidoob9203542018-09-17 22:56:37 -0400353
khenaidoo92e62c52018-10-03 14:02:54 -0400354 # device = self.adapter_agent.get_device(device.id)
355 # device.oper_status = OperStatus.ACTIVE
356 # self.adapter_agent.update_device(device)
khenaidoob9203542018-09-17 22:56:37 -0400357
khenaidoo92e62c52018-10-03 14:02:54 -0400358 yield self.adapter_agent.device_state_update(self.device_id, oper_status=OperStatus.ACTIVE, connect_status=ConnectStatus.REACHABLE)
359
360 self.log.info('re-enabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400361 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)