blob: 4e8b2ddaa3ca8ea14d1d332396ab0ed43240310d [file] [log] [blame]
Matt Jeannerete6a70332018-07-20 16:11:25 -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"""
18Broadcom OpenOMCI OLT/ONU adapter handler.
19"""
20
21import structlog
22from twisted.internet import reactor, task
23from twisted.internet.defer import DeferredQueue, inlineCallbacks, returnValue, TimeoutError
24
Matt Jeannerete6a70332018-07-20 16:11:25 -040025from common.utils.indexpool import IndexPool
Matt Jeannerete6a70332018-07-20 16:11:25 -040026import voltha.core.flow_decomposer as fd
27from voltha.registry import registry
28from voltha.protos import third_party
Matt Jeanneretf4448402018-09-25 12:51:19 -040029from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
Matt Jeannerete6a70332018-07-20 16:11:25 -040030from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port
Matt Jeannerete6a70332018-07-20 16:11:25 -040031from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
32from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
33from voltha.extensions.omci.onu_configuration import OMCCVersion
34from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
35 OnuDeviceEntry, IN_SYNC_KEY
Matt Jeannerete6a70332018-07-20 16:11:25 -040036from voltha.adapters.brcm_openomci_onu.omci.brcm_mib_download_task import BrcmMibDownloadTask
Matt Jeanneret6da55792018-08-08 16:44:18 -040037from voltha.adapters.brcm_openomci_onu.omci.brcm_uni_lock_task import BrcmUniLockTask
Matt Jeanneretf4448402018-09-25 12:51:19 -040038from voltha.adapters.brcm_openomci_onu.omci.brcm_vlan_filter_task import BrcmVlanFilterTask
Matt Jeannerete6a70332018-07-20 16:11:25 -040039from voltha.adapters.brcm_openomci_onu.onu_gem_port import *
40from voltha.adapters.brcm_openomci_onu.onu_tcont import *
41from voltha.adapters.brcm_openomci_onu.pon_port import *
42from voltha.adapters.brcm_openomci_onu.uni_port import *
43from voltha.adapters.brcm_openomci_onu.onu_traffic_descriptor import *
44
Matt Jeannerete6a70332018-07-20 16:11:25 -040045OP = EntityOperations
46RC = ReasonCodes
47
48
49_ = third_party
50log = structlog.get_logger()
51
52
53BRDCM_DEFAULT_VLAN = 4091
54ADMIN_STATE_LOCK = 1
55ADMIN_STATE_UNLOCK = 0
56RESERVED_VLAN_ID = 4095
Matt Jeanneret94f8d292018-08-31 12:49:27 -040057_STARTUP_RETRY_WAIT = 20
Matt Jeannerete6a70332018-07-20 16:11:25 -040058_MAXIMUM_PORT = 128 # UNI ports
59
60
61
62_ = third_party
63
64
65
66class BrcmOpenomciOnuHandler(object):
67
68 def __init__(self, adapter, device_id):
69 self.log = structlog.get_logger(device_id=device_id)
70 self.log.debug('function-entry')
71 self.adapter = adapter
72 self.adapter_agent = adapter.adapter_agent
73 self.device_id = device_id
74 self.incoming_messages = DeferredQueue()
75 self.event_messages = DeferredQueue()
76 self.proxy_address = None
77 self.tx_id = 0
78 self._enabled = False
79 self._omcc_version = OMCCVersion.Unknown
80 self._total_tcont_count = 0 # From ANI-G ME
81 self._qos_flexibility = 0 # From ONT2_G ME
82
83 self._onu_indication = None
84 self._unis = dict() # Port # -> UniPort
85 self._port_number_pool = IndexPool(_MAXIMUM_PORT, 0)
86
87 self._pon = None
88 #TODO: probably shouldnt be hardcoded, determine from olt maybe?
89 self._pon_port_number = 100
90 self.logical_device_id = None
91
92 # Set up OpenOMCI environment
93 self._onu_omci_device = None
94 self._dev_info_loaded = False
95 self._deferred = None
96
97 self._in_sync_subscription = None
98 self._connectivity_subscription = None
99 self._capabilities_subscription = None
100
101 @property
102 def enabled(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400103 return self._enabled
104
105 @enabled.setter
106 def enabled(self, value):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400107 if self._enabled != value:
108 self._enabled = value
109
110 @property
111 def omci_agent(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400112 return self.adapter.omci_agent
113
114 @property
115 def omci_cc(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400116 return self._onu_omci_device.omci_cc if self._onu_omci_device is not None else None
117
118 @property
119 def uni_ports(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400120 return self._unis.values()
121
122 def uni_port(self, port_no_or_name):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400123 if isinstance(port_no_or_name, (str, unicode)):
124 return next((uni for uni in self.uni_ports
125 if uni.name == port_no_or_name), None)
126
127 assert isinstance(port_no_or_name, int), 'Invalid parameter type'
128 return self._unis.get(port_no_or_name)
129
130 @property
131 def pon_port(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400132 return self._pon
133
134 @property
135 def _next_port_number(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400136 return self._port_number_pool.get_next()
137
138 def _release_port_number(self, number):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400139 self._port_number_pool.release(number)
140
141 def receive_message(self, msg):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400142 if self.omci_cc is not None:
143 self.omci_cc.receive_message(msg)
144
Matt Jeanneret54c82662018-08-23 11:21:19 -0400145 # Called once when the adapter creates the device/onu instance
Matt Jeannerete6a70332018-07-20 16:11:25 -0400146 def activate(self, device):
147 self.log.debug('function-entry', device=device)
148
149 # first we verify that we got parent reference and proxy info
150 assert device.parent_id
151 assert device.proxy_address.device_id
152
153 # register for proxied messages right away
154 self.proxy_address = device.proxy_address
155 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
156
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400157 if self.enabled is not True:
158 self.log.info('activating-new-onu')
159 # populate what we know. rest comes later after mib sync
160 device.root = True
161 device.vendor = 'Broadcom'
162 device.connect_status = ConnectStatus.REACHABLE
Matt Jeanneret6da55792018-08-08 16:44:18 -0400163 device.oper_status = OperStatus.DISCOVERED
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400164 device.reason = 'activating-onu'
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400165 self.adapter_agent.update_device(device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400166
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400167 self.log.debug('set-device-discovered')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400168
Matt Jeanneret54c82662018-08-23 11:21:19 -0400169 self._init_pon_state(device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400170
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400171 self.enabled = True
172 else:
173 self.log.info('onu-already-activated')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400174
Matt Jeannerete6a70332018-07-20 16:11:25 -0400175
Matt Jeanneret54c82662018-08-23 11:21:19 -0400176 # Called once when the adapter needs to re-create device. usually on vcore restart
Matt Jeannerete6a70332018-07-20 16:11:25 -0400177 def reconcile(self, device):
178 self.log.debug('function-entry', device=device)
179
180 # first we verify that we got parent reference and proxy info
181 assert device.parent_id
182 assert device.proxy_address.device_id
183
184 # register for proxied messages right away
185 self.proxy_address = device.proxy_address
186 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
187
Matt Jeanneret54c82662018-08-23 11:21:19 -0400188 if self.enabled is not True:
189 self.log.info('reconciling-broadcom-onu-device')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400190
Matt Jeanneret54c82662018-08-23 11:21:19 -0400191 self._init_pon_state(device)
192
193 # need to restart state machines on vcore restart. there is no indication to do it for us.
194 self._onu_omci_device.start()
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400195 device.reason = "restarting-openomci"
196 self.adapter_agent.update_device(device)
Matt Jeanneret54c82662018-08-23 11:21:19 -0400197
198 # TODO: this is probably a bit heavy handed
199 # Force a reboot for now. We need indications to reflow to reassign tconts and gems given vcore went away
200 # This may not be necessary when mib resync actually works
201 reactor.callLater(1, self.reboot)
202
203 self.enabled = True
204 else:
205 self.log.info('onu-already-activated')
206
207
208 def _init_pon_state(self, device):
209 self.log.debug('function-entry', device=device)
210
211 self._pon = PonPort.create(self, self._pon_port_number)
212 self.adapter_agent.add_port(device.id, self._pon.get_port())
213
214 self.log.debug('added-pon-port-to-agent', pon=self._pon)
215
216 parent_device = self.adapter_agent.get_device(device.parent_id)
217 self.logical_device_id = parent_device.parent_id
218
219 self.adapter_agent.update_device(device)
220
221 # Create and start the OpenOMCI ONU Device Entry for this ONU
222 self._onu_omci_device = self.omci_agent.add_device(self.device_id,
223 self.adapter_agent,
224 support_classes=self.adapter.broadcom_omci)
225 # Port startup
226 if self._pon is not None:
227 self._pon.enabled = True
Matt Jeannerete6a70332018-07-20 16:11:25 -0400228
229 # TODO: move to UniPort
230 def update_logical_port(self, logical_device_id, port_id, state):
231 try:
232 self.log.info('updating-logical-port', logical_port_id=port_id,
233 logical_device_id=logical_device_id, state=state)
234 logical_port = self.adapter_agent.get_logical_port(logical_device_id,
235 port_id)
236 logical_port.ofp_port.state = state
237 self.adapter_agent.update_logical_port(logical_device_id,
238 logical_port)
239 except Exception as e:
240 self.log.exception("exception-updating-port",e=e)
241
Matt Jeanneret6da55792018-08-08 16:44:18 -0400242 @inlineCallbacks
Matt Jeannerete6a70332018-07-20 16:11:25 -0400243 def delete(self, device):
Matt Jeanneret6da55792018-08-08 16:44:18 -0400244 self.log.info('delete-onu', device=device)
245
246 parent_device = self.adapter_agent.get_device(device.parent_id)
247 if parent_device.type == 'openolt':
248 parent_adapter = registry('adapter_loader').get_agent(parent_device.adapter).adapter
249 self.log.debug('parent-adapter-delete-onu', onu_device=device,
250 parent_device=parent_device,
251 parent_adapter=parent_adapter)
252 try:
253 parent_adapter.delete_child_device(parent_device.id, device)
254 except AttributeError:
255 self.log.debug('parent-device-delete-child-not-implemented')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400256
Matt Jeanneret54c82662018-08-23 11:21:19 -0400257 # Calling this assumes the onu is active/ready and had at least an initial mib downloaded. This gets called from
258 # flow decomposition that ultimately comes from onos
Matt Jeannerete6a70332018-07-20 16:11:25 -0400259 @inlineCallbacks
260 def update_flow_table(self, device, flows):
261 self.log.debug('function-entry', device=device, flows=flows)
262 #
263 # We need to proxy through the OLT to get to the ONU
264 # Configuration from here should be using OMCI
265 #
266 #self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
267
268 def is_downstream(port):
269 return port == self._pon_port_number
270
271 def is_upstream(port):
272 return not is_downstream(port)
273
274 for flow in flows:
275 _type = None
276 _port = None
277 _vlan_vid = None
278 _udp_dst = None
279 _udp_src = None
280 _ipv4_dst = None
281 _ipv4_src = None
282 _metadata = None
283 _output = None
284 _push_tpid = None
285 _field = None
286 _set_vlan_vid = None
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400287 self.log.debug('bulk-flow-update', device_id=device.id, flow=flow)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400288 try:
289 _in_port = fd.get_in_port(flow)
290 assert _in_port is not None
291
292 if is_downstream(_in_port):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400293 self.log.debug('downstream-flow')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400294 elif is_upstream(_in_port):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400295 self.log.debug('upstream-flow')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400296 else:
297 raise Exception('port should be 1 or 2 by our convention')
298
299 _out_port = fd.get_out_port(flow) # may be None
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400300 self.log.debug('out-port', out_port=_out_port)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400301
302 for field in fd.get_ofb_fields(flow):
303 if field.type == fd.ETH_TYPE:
304 _type = field.eth_type
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400305 self.log.debug('field-type-eth-type',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400306 eth_type=_type)
307
308 elif field.type == fd.IP_PROTO:
309 _proto = field.ip_proto
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400310 self.log.debug('field-type-ip-proto',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400311 ip_proto=_proto)
312
313 elif field.type == fd.IN_PORT:
314 _port = field.port
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400315 self.log.debug('field-type-in-port',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400316 in_port=_port)
317
318 elif field.type == fd.VLAN_VID:
319 _vlan_vid = field.vlan_vid & 0xfff
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400320 self.log.debug('field-type-vlan-vid',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400321 vlan=_vlan_vid)
322
323 elif field.type == fd.VLAN_PCP:
324 _vlan_pcp = field.vlan_pcp
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400325 self.log.debug('field-type-vlan-pcp',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400326 pcp=_vlan_pcp)
327
328 elif field.type == fd.UDP_DST:
329 _udp_dst = field.udp_dst
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400330 self.log.debug('field-type-udp-dst',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400331 udp_dst=_udp_dst)
332
333 elif field.type == fd.UDP_SRC:
334 _udp_src = field.udp_src
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400335 self.log.debug('field-type-udp-src',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400336 udp_src=_udp_src)
337
338 elif field.type == fd.IPV4_DST:
339 _ipv4_dst = field.ipv4_dst
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400340 self.log.debug('field-type-ipv4-dst',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400341 ipv4_dst=_ipv4_dst)
342
343 elif field.type == fd.IPV4_SRC:
344 _ipv4_src = field.ipv4_src
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400345 self.log.debug('field-type-ipv4-src',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400346 ipv4_dst=_ipv4_src)
347
348 elif field.type == fd.METADATA:
349 _metadata = field.table_metadata
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400350 self.log.debug('field-type-metadata',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400351 metadata=_metadata)
352
353 else:
354 raise NotImplementedError('field.type={}'.format(
355 field.type))
356
357 for action in fd.get_actions(flow):
358
359 if action.type == fd.OUTPUT:
360 _output = action.output.port
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400361 self.log.debug('action-type-output',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400362 output=_output, in_port=_in_port)
363
364 elif action.type == fd.POP_VLAN:
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400365 self.log.debug('action-type-pop-vlan',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400366 in_port=_in_port)
367
368 elif action.type == fd.PUSH_VLAN:
369 _push_tpid = action.push.ethertype
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400370 self.log.debug('action-type-push-vlan',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400371 push_tpid=_push_tpid, in_port=_in_port)
372 if action.push.ethertype != 0x8100:
373 self.log.error('unhandled-tpid',
374 ethertype=action.push.ethertype)
375
376 elif action.type == fd.SET_FIELD:
377 _field = action.set_field.field.ofb_field
378 assert (action.set_field.field.oxm_class ==
379 OFPXMC_OPENFLOW_BASIC)
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400380 self.log.debug('action-type-set-field',
Matt Jeannerete6a70332018-07-20 16:11:25 -0400381 field=_field, in_port=_in_port)
382 if _field.type == fd.VLAN_VID:
383 _set_vlan_vid = _field.vlan_vid & 0xfff
Matt Jeanneretf4448402018-09-25 12:51:19 -0400384 self.log.debug('set-field-type-vlan-vid', _set_vlan_vid)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400385 else:
386 self.log.error('unsupported-action-set-field-type',
387 field_type=_field.type)
388 else:
389 self.log.error('unsupported-action-type',
390 action_type=action.type, in_port=_in_port)
391
Matt Jeanneretf4448402018-09-25 12:51:19 -0400392 # TODO: We only set vlan omci flows. Handle omci matching ethertypes at some point in another task
Nicolas Palpacuer6093c1a2018-08-27 15:03:24 -0400393 if _type is not None:
Matt Jeanneretf4448402018-09-25 12:51:19 -0400394 self.log.warn('ignoring-flow-with-ethType', ethType=_type)
395 elif _set_vlan_vid is None or _set_vlan_vid == 0:
396 self.log.warn('ignorning-flow-that-does-not-set-vlanid')
397 else:
398 self._add_vlan_filter_task(device, _set_vlan_vid)
Saurav Das6c539962018-09-13 14:17:41 -0700399
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400400
Matt Jeannerete6a70332018-07-20 16:11:25 -0400401 except Exception as e:
402 self.log.exception('failed-to-install-flow', e=e, flow=flow)
403
Matt Jeanneretf4448402018-09-25 12:51:19 -0400404 def _add_vlan_filter_task(self, device, _set_vlan_vid):
405 def success(_results):
406 self.log.info('vlan-tagging-success', _results=_results)
407 device.reason = 'omci-flows-pushed'
408 self._vlan_filter_task = None
409
410 def failure(_reason):
411 self.log.warn('vlan-tagging-failure', _reason=_reason)
412 device.reason = 'omci-flows-failed-retrying'
413 self._vlan_filter_task = reactor.callLater(_STARTUP_RETRY_WAIT,
414 self._add_vlan_filter_task, device, _set_vlan_vid)
415 self.log.info('setting-vlan-tag')
416 self._vlan_filter_task = BrcmVlanFilterTask(self.omci_agent, self.device_id, _set_vlan_vid)
417 self._deferred = self._onu_omci_device.task_runner.queue_task(self._vlan_filter_task)
418 self._deferred.addCallbacks(success, failure)
419
Matt Jeannerete6a70332018-07-20 16:11:25 -0400420 def get_tx_id(self):
421 self.log.debug('function-entry')
422 self.tx_id += 1
423 return self.tx_id
424
Matt Jeanneret04ba4fd2018-08-16 15:32:21 -0400425 # TODO: Actually conform to or create a proper interface.
426 # this and the other functions called from the olt arent very clear.
Matt Jeanneret54c82662018-08-23 11:21:19 -0400427 # Called each time there is an onu "up" indication from the olt handler
Matt Jeannerete6a70332018-07-20 16:11:25 -0400428 def create_interface(self, data):
429 self.log.debug('function-entry', data=data)
430 self._onu_indication = data
431
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400432 onu_device = self.adapter_agent.get_device(self.device_id)
433
Matt Jeanneret6da55792018-08-08 16:44:18 -0400434 self.log.debug('starting-openomci-statemachine')
435 self._subscribe_to_events()
436 reactor.callLater(1, self._onu_omci_device.start)
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400437 onu_device.reason = "starting-openomci"
438 self.adapter_agent.update_device(onu_device)
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400439
Matt Jeanneret54c82662018-08-23 11:21:19 -0400440 # Currently called each time there is an onu "down" indication from the olt handler
441 # TODO: possibly other reasons to "update" from the olt?
Matt Jeannerete6a70332018-07-20 16:11:25 -0400442 def update_interface(self, data):
443 self.log.debug('function-entry', data=data)
Nicolas Palpacuer08cc8182018-08-22 10:22:19 -0400444 oper_state = data.get('oper_state', None)
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400445
446 onu_device = self.adapter_agent.get_device(self.device_id)
447
Nicolas Palpacuer08cc8182018-08-22 10:22:19 -0400448 if oper_state == 'down':
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400449 self.log.debug('stopping-openomci-statemachine')
Matt Jeanneret6da55792018-08-08 16:44:18 -0400450 reactor.callLater(0, self._onu_omci_device.stop)
451 self.disable_ports(onu_device)
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400452 onu_device.reason = "stopping-openomci"
Matt Jeanneret6da55792018-08-08 16:44:18 -0400453 onu_device.connect_status = ConnectStatus.UNREACHABLE
454 onu_device.oper_status = OperStatus.DISCOVERED
455 self.adapter_agent.update_device(onu_device)
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400456 else:
457 self.log.debug('not-changing-openomci-statemachine')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400458
Matt Jeanneret54c82662018-08-23 11:21:19 -0400459 # Not currently called by olt or anything else
Matt Jeannerete6a70332018-07-20 16:11:25 -0400460 def remove_interface(self, data):
461 self.log.debug('function-entry', data=data)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400462
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400463 onu_device = self.adapter_agent.get_device(self.device_id)
464
465 self.log.debug('stopping-openomci-statemachine')
466 reactor.callLater(0, self._onu_omci_device.stop)
Matt Jeanneret6da55792018-08-08 16:44:18 -0400467 self.disable_ports(onu_device)
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400468 onu_device.reason = "stopping-openomci"
469 self.adapter_agent.update_device(onu_device)
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400470
471 # TODO: im sure there is more to do here
472
Matt Jeanneret54c82662018-08-23 11:21:19 -0400473 # Called when there is an olt up indication, providing the gem port id chosen by the olt handler
Matt Jeannerete6a70332018-07-20 16:11:25 -0400474 def create_gemport(self, data):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400475 self.log.debug('create-gemport', data=data)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400476 gem_portdata = GemportsConfigData()
477 gem_portdata.CopyFrom(data)
478
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400479 # TODO: fill in what i have. This needs to be provided from the OLT
480 # currently its hardcoded/static
481 gemdict = dict()
Matt Jeannerete6a70332018-07-20 16:11:25 -0400482 gemdict['gemport-id'] = gem_portdata.gemport_id
483 gemdict['encryption'] = gem_portdata.aes_indicator
484 gemdict['tcont-ref'] = int(gem_portdata.tcont_ref)
485 gemdict['name'] = gem_portdata.gemport_id
486 gemdict['traffic-class'] = gem_portdata.traffic_class
487 gemdict['traffic-class'] = gem_portdata.traffic_class
488
489 gem_port = OnuGemPort.create(self, gem_port=gemdict, entity_id=self._pon.next_gem_entity_id)
490
491 self._pon.add_gem_port(gem_port)
492
493 self.log.debug('pon-add-gemport', gem_port=gem_port)
494
495
Matt Jeanneret54c82662018-08-23 11:21:19 -0400496 # Not currently called. Would be called presumably from the olt handler
Matt Jeannerete6a70332018-07-20 16:11:25 -0400497 @inlineCallbacks
498 def remove_gemport(self, data):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400499 self.log.debug('remove-gemport', data=data)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400500 gem_port = GemportsConfigData()
501 gem_port.CopyFrom(data)
502 device = self.adapter_agent.get_device(self.device_id)
503 if device.connect_status != ConnectStatus.REACHABLE:
504 self.log.error('device-unreachable')
505 returnValue(None)
506
507 #TODO: Create a remove task that encompasses this
508
Matt Jeanneret54c82662018-08-23 11:21:19 -0400509 # Called when there is an olt up indication, providing the tcont id chosen by the olt handler
Matt Jeannerete6a70332018-07-20 16:11:25 -0400510 def create_tcont(self, tcont_data, traffic_descriptor_data):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400511 self.log.debug('create-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400512 tcontdata = TcontsConfigData()
513 tcontdata.CopyFrom(tcont_data)
514
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400515 # TODO: fill in what i have. This needs to be provided from the OLT
516 # currently its hardcoded/static
517 tcontdict = dict()
Matt Jeannerete6a70332018-07-20 16:11:25 -0400518 tcontdict['alloc-id'] = tcontdata.alloc_id
519 tcontdict['name'] = tcontdata.name
520 tcontdict['vont-ani'] = tcontdata.interface_reference
521
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400522 # TODO: Not sure what to do with any of this...
523 tddata = dict()
Matt Jeannerete6a70332018-07-20 16:11:25 -0400524 tddata['name'] = 'not-sure-td-profile'
525 tddata['fixed-bandwidth'] = "not-sure-fixed"
526 tddata['assured-bandwidth'] = "not-sure-assured"
527 tddata['maximum-bandwidth'] = "not-sure-max"
528 tddata['additional-bw-eligibility-indicator'] = "not-sure-additional"
529
530 td = OnuTrafficDescriptor.create(tddata)
531 tcont = OnuTCont.create(self, tcont=tcontdict, td=td)
532
533 self._pon.add_tcont(tcont)
534
535 self.log.debug('pon-add-tcont', tcont=tcont)
536
537 if tcontdata.interface_reference is not None:
538 self.log.debug('tcont', tcont=tcont.alloc_id)
539 else:
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400540 self.log.info('received-null-tcont-data', tcont=tcont.alloc_id)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400541
Matt Jeanneret54c82662018-08-23 11:21:19 -0400542 # Not currently called. Would be called presumably from the olt handler
Matt Jeannerete6a70332018-07-20 16:11:25 -0400543 @inlineCallbacks
544 def remove_tcont(self, tcont_data, traffic_descriptor_data):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400545 self.log.debug('remove-tcont', tcont_data=tcont_data, traffic_descriptor_data=traffic_descriptor_data)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400546 device = self.adapter_agent.get_device(self.device_id)
547 if device.connect_status != ConnectStatus.REACHABLE:
548 self.log.error('device-unreachable')
549 returnValue(None)
550
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400551 # TODO: Create some omci task that encompases this what intended
Matt Jeannerete6a70332018-07-20 16:11:25 -0400552
Matt Jeanneret54c82662018-08-23 11:21:19 -0400553 # Not currently called. Would be called presumably from the olt handler
Matt Jeannerete6a70332018-07-20 16:11:25 -0400554 def create_multicast_gemport(self, data):
555 self.log.debug('function-entry', data=data)
556
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400557 # TODO: create objects and populate for later omci calls
Matt Jeannerete6a70332018-07-20 16:11:25 -0400558
Matt Jeannerete6a70332018-07-20 16:11:25 -0400559 @inlineCallbacks
560 def disable(self, device):
561 self.log.debug('function-entry', device=device)
562 try:
Matt Jeanneret6da55792018-08-08 16:44:18 -0400563 self.log.info('sending-uni-lock-towards-device', device=device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400564
Matt Jeanneret6da55792018-08-08 16:44:18 -0400565 def stop_anyway(reason):
566 # proceed with disable regardless if we could reach the onu. for example onu is unplugged
567 self.log.debug('stopping-openomci-statemachine')
568 reactor.callLater(0, self._onu_omci_device.stop)
569 self.disable_ports(device)
570 device.oper_status = OperStatus.UNKNOWN
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400571 device.reason = "omci-admin-lock"
Matt Jeanneret6da55792018-08-08 16:44:18 -0400572 self.adapter_agent.update_device(device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400573
Matt Jeanneret6da55792018-08-08 16:44:18 -0400574 # lock all the unis
575 task = BrcmUniLockTask(self.omci_agent, self.device_id, lock=True)
576 self._deferred = self._onu_omci_device.task_runner.queue_task(task)
577 self._deferred.addCallbacks(stop_anyway, stop_anyway)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400578 except Exception as e:
579 log.exception('exception-in-onu-disable', exception=e)
580
581 @inlineCallbacks
582 def reenable(self, device):
583 self.log.debug('function-entry', device=device)
584 try:
Matt Jeannerete6a70332018-07-20 16:11:25 -0400585 # Start up OpenOMCI state machines for this device
Matt Jeanneret6da55792018-08-08 16:44:18 -0400586 # this will ultimately resync mib and unlock unis on successful redownloading the mib
587 self.log.debug('restarting-openomci-statemachine')
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400588 self._subscribe_to_events()
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400589 device.reason = "restarting-openomci"
590 self.adapter_agent.update_device(device)
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400591 reactor.callLater(1, self._onu_omci_device.start)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400592 except Exception as e:
593 log.exception('exception-in-onu-reenable', exception=e)
594
595 @inlineCallbacks
596 def reboot(self):
Matt Jeannerete6a70332018-07-20 16:11:25 -0400597 self.log.info('reboot-device')
598 device = self.adapter_agent.get_device(self.device_id)
599 if device.connect_status != ConnectStatus.REACHABLE:
Matt Jeanneret54c82662018-08-23 11:21:19 -0400600 self.log.error("device-unreachable")
Matt Jeannerete6a70332018-07-20 16:11:25 -0400601 returnValue(None)
602
Matt Jeanneret6da55792018-08-08 16:44:18 -0400603 def success(_results):
604 self.log.info('reboot-success', _results=_results)
605 self.disable_ports(device)
606 device.connect_status = ConnectStatus.UNREACHABLE
607 device.oper_status = OperStatus.DISCOVERED
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400608 device.reason = "rebooting"
Matt Jeanneret6da55792018-08-08 16:44:18 -0400609 self.adapter_agent.update_device(device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400610
Matt Jeanneret6da55792018-08-08 16:44:18 -0400611 def failure(_reason):
612 self.log.info('reboot-failure', _reason=_reason)
613
614 self._deferred = self._onu_omci_device.reboot()
615 self._deferred.addCallbacks(success, failure)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400616
617 def disable_ports(self, onu_device):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400618 self.log.info('disable-ports', device_id=self.device_id,
619 onu_device=onu_device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400620
621 # Disable all ports on that device
622 self.adapter_agent.disable_all_ports(self.device_id)
623
624 parent_device = self.adapter_agent.get_device(onu_device.parent_id)
625 assert parent_device
626 logical_device_id = parent_device.parent_id
627 assert logical_device_id
628 ports = self.adapter_agent.get_ports(onu_device.id, Port.ETHERNET_UNI)
629 for port in ports:
630 port_id = 'uni-{}'.format(port.port_no)
631 # TODO: move to UniPort
632 self.update_logical_port(logical_device_id, port_id, OFPPS_LINK_DOWN)
633
634 def enable_ports(self, onu_device):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400635 self.log.info('enable-ports', device_id=self.device_id, onu_device=onu_device)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400636
637 # Disable all ports on that device
638 self.adapter_agent.enable_all_ports(self.device_id)
639
640 parent_device = self.adapter_agent.get_device(onu_device.parent_id)
641 assert parent_device
642 logical_device_id = parent_device.parent_id
643 assert logical_device_id
644 ports = self.adapter_agent.get_ports(onu_device.id, Port.ETHERNET_UNI)
645 for port in ports:
646 port_id = 'uni-{}'.format(port.port_no)
647 # TODO: move to UniPort
648 self.update_logical_port(logical_device_id, port_id, OFPPS_LIVE)
649
650
Matt Jeanneret54c82662018-08-23 11:21:19 -0400651 # Called just before openomci state machine is started. These listen for events from selected state machines,
652 # most importantly, mib in sync. Which ultimately leads to downloading the mib
Matt Jeannerete6a70332018-07-20 16:11:25 -0400653 def _subscribe_to_events(self):
654 self.log.debug('function-entry')
655
656 # OMCI MIB Database sync status
657 bus = self._onu_omci_device.event_bus
658 topic = OnuDeviceEntry.event_bus_topic(self.device_id,
659 OnuDeviceEvents.MibDatabaseSyncEvent)
660 self._in_sync_subscription = bus.subscribe(topic, self.in_sync_handler)
661
662 # OMCI Capabilities
663 bus = self._onu_omci_device.event_bus
664 topic = OnuDeviceEntry.event_bus_topic(self.device_id,
665 OnuDeviceEvents.OmciCapabilitiesEvent)
666 self._capabilities_subscription = bus.subscribe(topic, self.capabilties_handler)
667
668 def _unsubscribe_to_events(self):
669 self.log.debug('function-entry')
670 if self._in_sync_subscription is not None:
671 bus = self._onu_omci_device.event_bus
672 bus.unsubscribe(self._in_sync_subscription)
673 self._in_sync_subscription = None
674
Matt Jeanneret54c82662018-08-23 11:21:19 -0400675 # Called when the mib is in sync
Matt Jeannerete6a70332018-07-20 16:11:25 -0400676 def in_sync_handler(self, _topic, msg):
677 self.log.debug('function-entry', _topic=_topic, msg=msg)
678 if self._in_sync_subscription is not None:
679 try:
680 in_sync = msg[IN_SYNC_KEY]
681
682 if in_sync:
683 # Only call this once
684 bus = self._onu_omci_device.event_bus
685 bus.unsubscribe(self._in_sync_subscription)
686 self._in_sync_subscription = None
687
688 # Start up device_info load
689 self.log.debug('running-mib-sync')
690 reactor.callLater(0, self._mib_in_sync)
691
692 except Exception as e:
693 self.log.exception('in-sync', e=e)
694
695 def capabilties_handler(self, _topic, _msg):
696 self.log.debug('function-entry', _topic=_topic, msg=_msg)
697 if self._capabilities_subscription is not None:
698 self.log.debug('capabilities-handler-done')
699
Matt Jeanneret54c82662018-08-23 11:21:19 -0400700 # Mib is in sync, we can now query what we learned and actually start pushing ME (download) to the ONU.
701 # Currently uses a basic mib download task that create a bridge with a single gem port and uni, only allowing EAP
702 # Implement your own MibDownloadTask if you wish to setup something different by default
Matt Jeannerete6a70332018-07-20 16:11:25 -0400703 def _mib_in_sync(self):
704 self.log.debug('function-entry')
Matt Jeannerete6a70332018-07-20 16:11:25 -0400705
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400706 omci = self._onu_omci_device
707 in_sync = omci.mib_db_in_sync
708
Matt Jeanneret6da55792018-08-08 16:44:18 -0400709 device = self.adapter_agent.get_device(self.device_id)
710 device.reason = 'discovery-mibsync-complete'
711 self.adapter_agent.update_device(device)
712
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400713 if not self._dev_info_loaded:
714 self.log.info('loading-device-data-from-mib', in_sync=in_sync, already_loaded=self._dev_info_loaded)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400715
Matt Jeannerete6a70332018-07-20 16:11:25 -0400716 omci_dev = self._onu_omci_device
717 config = omci_dev.configuration
718
Matt Jeanneret54c82662018-08-23 11:21:19 -0400719 # TODO: run this sooner somehow. shouldnt have to wait for mib sync to push an initial download
Matt Jeannerete6a70332018-07-20 16:11:25 -0400720 # In Sync, we can register logical ports now. Ideally this could occur on
721 # the first time we received a successful (no timeout) OMCI Rx response.
722 try:
Matt Jeannerete6a70332018-07-20 16:11:25 -0400723
724 ani_g = config.ani_g_entities
725 uni_g = config.uni_g_entities
726 pptp = config.pptp_entities
727
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400728 # TODO: there are onu out there that have uni ports available but no pptp. These all-in-one devices
729 # likely need virtual ethernet interface point ME #329 configured rather than pptp.
730 if ani_g is None or uni_g is None or pptp is None:
731 device.reason = 'onu-missing-required-elements'
732 self.adapter_agent.update_device(device)
733 raise Exception("onu-missing-required-elements")
734
Matt Jeanneret54c82662018-08-23 11:21:19 -0400735 # Currently logging the ani and uni for information purposes. Actually act on the pptp as its ME ID
736 # is the most correct one to use in later tasks.
Matt Jeannerete6a70332018-07-20 16:11:25 -0400737 for key, value in ani_g.iteritems():
738 self.log.debug("discovered-ani", key=key, value=value)
739
740 for key, value in uni_g.iteritems():
741 self.log.debug("discovered-uni", key=key, value=value)
742
743 for key, value in pptp.iteritems():
744 self.log.debug("discovered-pptp-uni", key=key, value=value)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400745 entity_id = key
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400746 self._add_uni_port(entity_id)
Matt Jeannerete6a70332018-07-20 16:11:25 -0400747
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400748 # TODO: only one uni/pptp for now. flow bug in openolt
Matt Jeannerete6a70332018-07-20 16:11:25 -0400749 break
750
751 self._total_tcont_count = ani_g.get('total-tcont-count')
752 self._qos_flexibility = config.qos_configuration_flexibility or 0
753 self._omcc_version = config.omcc_version or OMCCVersion.Unknown
754 self.log.debug("set-total-tcont-count", tcont_count=self._total_tcont_count)
755
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400756 self._dev_info_loaded = True
Matt Jeannerete6a70332018-07-20 16:11:25 -0400757
Matt Jeannerete6a70332018-07-20 16:11:25 -0400758 except Exception as e:
759 self.log.exception('device-info-load', e=e)
760 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync)
761
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400762 else:
763 self.log.info('device-info-already-loaded', in_sync=in_sync, already_loaded=self._dev_info_loaded)
764
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400765 if self._dev_info_loaded:
766 if device.admin_state == AdminState.ENABLED:
767 def success(_results):
768 self.log.info('mib-download-success', _results=_results)
769 device = self.adapter_agent.get_device(self.device_id)
770 device.reason = 'initial-mib-downloaded'
771 device.oper_status = OperStatus.ACTIVE
772 device.connect_status = ConnectStatus.REACHABLE
773 self.enable_ports(device)
774 self.adapter_agent.update_device(device)
775 self._mib_download_task = None
Matt Jeanneret12cd5d02018-08-07 15:30:19 -0400776
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400777 def failure(_reason):
Matt Jeanneretf4448402018-09-25 12:51:19 -0400778 self.log.warn('mib-download-failure-retrying', _reason=_reason)
779 device.reason = 'initial-mib-download-failure-retrying'
780 self.adapter_agent.update_device(device)
781 self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self._mib_in_sync)
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400782
783 # Download an initial mib that creates simple bridge that can pass EAP. On success (above) finally set
784 # the device to active/reachable. This then opens up the handler to openflow pushes from outside
785 self.log.info('downloading-initial-mib-configuration')
Matt Jeanneret04ba4fd2018-08-16 15:32:21 -0400786 self._mib_download_task = BrcmMibDownloadTask(self.omci_agent, self)
787 self._deferred = self._onu_omci_device.task_runner.queue_task(self._mib_download_task)
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400788 self._deferred.addCallbacks(success, failure)
789 else:
790 self.log.info('admin-down-disabling')
791 self.disable(device)
Matt Jeanneret04ba4fd2018-08-16 15:32:21 -0400792 else:
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400793 self.log.info('device-info-not-loaded-skipping-mib-download')
794
795
796 def _add_uni_port(self, entity_id):
797 self.log.debug('function-entry')
798
799 device = self.adapter_agent.get_device(self.device_id)
800 parent_device = self.adapter_agent.get_device(device.parent_id)
801
802 parent_adapter_agent = registry('adapter_loader').get_agent(parent_device.adapter)
803 if parent_adapter_agent is None:
804 self.log.error('openolt_adapter_agent-could-not-be-retrieved')
805
806 # TODO: This knowledge is locked away in openolt. and it assumes one onu equals one uni...
Shad Ansaricd20a6d2018-10-02 14:36:33 +0000807 parent_device = self.adapter_agent.get_device(device.parent_id)
808 parent_adapter = parent_adapter_agent.adapter.devices[parent_device.id]
809 uni_no_start = parent_adapter.platform.mk_uni_port_num(
810 self._onu_indication.intf_id, self._onu_indication.onu_id)
Matt Jeanneret94f8d292018-08-31 12:49:27 -0400811
812 # TODO: Some or parts of this likely need to move to UniPort. especially the format stuff
813 working_port = self._next_port_number
814 uni_no = uni_no_start + working_port
815 uni_name = "uni-{}".format(uni_no)
816
817 mac_bridge_port_num = working_port + 1
818
819 self.log.debug('live-port-number-ready', uni_no=uni_no, uni_name=uni_name)
820
821 uni_port = UniPort.create(self, uni_name, uni_no, uni_name, device.vlan, device.vlan)
822 uni_port.entity_id = entity_id
823 uni_port.enabled = True
824 uni_port.mac_bridge_port_num = mac_bridge_port_num
825 uni_port.add_logical_port(uni_port.port_number, subscriber_vlan=device.vlan)
826
827 self.log.debug("created-uni-port", uni=uni_port)
828
829 self.adapter_agent.add_port(device.id, uni_port.get_port())
830 parent_adapter_agent.add_port(device.parent_id, uni_port.get_port())
831
832 self._unis[uni_port.port_number] = uni_port
833
834 # TODO: this should be in the PonPortclass
835 pon_port = self._pon.get_port()
836 self.adapter_agent.delete_port_reference_from_parent(self.device_id,
837 pon_port)
838
839 pon_port.peers.extend([Port.PeerPort(device_id=device.parent_id,
840 port_no=uni_port.port_number)])
841
842 self._pon._port = pon_port
843
844 self.adapter_agent.add_port_reference_to_parent(self.device_id,
845 pon_port)
846 self.adapter_agent.update_device(device)