blob: 82dcd9b953a2fc06ede677534d3caf1d5540d824 [file] [log] [blame]
Chip Boling27275992017-09-22 15:17:04 -05001# Copyright 2017-present Adtran, Inc.
Chip Boling3e3b1a92017-05-16 11:51:18 -05002#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
Chip Boling252c7772017-08-16 10:13:17 -05007# http://www.apache.org/licenses/LICENSE-2.0
Chip Boling3e3b1a92017-05-16 11:51:18 -05008#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Chip Boling252c7772017-08-16 10:13:17 -050014
Chip Boling3e3b1a92017-05-16 11:51:18 -050015import json
Chip Boling3e3b1a92017-05-16 11:51:18 -050016import random
17
Chip Boling3e3b1a92017-05-16 11:51:18 -050018import structlog
Chip Bolingab8863d2018-03-22 14:50:31 -050019from port import AdtnPort
Chip Boling48646962017-08-20 09:41:18 -050020from twisted.internet import reactor, defer
Chip Bolingbffef5e2018-08-07 14:53:12 -050021from twisted.internet.defer import inlineCallbacks, returnValue
Chip Boling3e3b1a92017-05-16 11:51:18 -050022
23from adtran_olt_handler import AdtranOltHandler
Chip Bolingfd1fd372017-12-20 13:34:12 -060024from net.adtran_rest import RestInvalidResponseCode
Chip Boling3e3b1a92017-05-16 11:51:18 -050025from codec.olt_config import OltConfig
26from onu import Onu
Chip Bolingbffef5e2018-08-07 14:53:12 -050027from voltha.extensions.alarms.onu.onu_los_alarm import OnuLosAlarm
Chip Bolingbb15b512018-06-01 11:39:58 -050028from voltha.protos.common_pb2 import AdminState
Chip Boling3e3b1a92017-05-16 11:51:18 -050029from voltha.protos.device_pb2 import Port
30
Chip Bolingbb15b512018-06-01 11:39:58 -050031try:
Chip Bolingbffef5e2018-08-07 14:53:12 -050032 from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
Chip Bolingbb15b512018-06-01 11:39:58 -050033except ImportError:
Chip Bolingbffef5e2018-08-07 14:53:12 -050034 from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
Chip Bolingbb15b512018-06-01 11:39:58 -050035
Chip Boling3e3b1a92017-05-16 11:51:18 -050036
Chip Bolingab8863d2018-03-22 14:50:31 -050037class PonPort(AdtnPort):
Chip Boling3e3b1a92017-05-16 11:51:18 -050038 """
Chip Bolingab8863d2018-03-22 14:50:31 -050039 GPON Port
Chip Boling3e3b1a92017-05-16 11:51:18 -050040 """
41 MAX_ONUS_SUPPORTED = 256
42 DEFAULT_ENABLED = False
Chip Bolingfd1fd372017-12-20 13:34:12 -060043 MAX_DEPLOYMENT_RANGE = 25000 # Meters (OLT-PB maximum)
Chip Boling27275992017-09-22 15:17:04 -050044
45 _MCAST_ONU_ID = 253
46 _MCAST_ALLOC_BASE = 0x500
Chip Boling3e3b1a92017-05-16 11:51:18 -050047
Chip Bolingab8863d2018-03-22 14:50:31 -050048 _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
Chip Boling252c7772017-08-16 10:13:17 -050049 _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
50
Chip Bolingab8863d2018-03-22 14:50:31 -050051 def __init__(self, parent, **kwargs):
52
53 super(PonPort, self).__init__(parent, **kwargs)
54
55 assert 'pon-id' in kwargs, 'PON ID not found'
Chip Boling7294b252017-06-15 16:16:55 -050056
57 self._parent = parent
Chip Bolingab8863d2018-03-22 14:50:31 -050058 self._pon_id = kwargs['pon-id']
59 self.log = structlog.get_logger(device_id=parent.device_id, pon_id=self._pon_id)
60 self._port_no = kwargs['port_no']
61 self._name = 'xpon 0/{}'.format(self._pon_id+1)
62 self._label = 'pon-{}'.format(self._pon_id)
63
Chip Boling27275992017-09-22 15:17:04 -050064 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -050065 self._expedite_sync = False
66 self._expedite_count = 0
67
Chip Bolingab8863d2018-03-22 14:50:31 -050068 self._discovery_tick = 20.0
69 self._no_onu_discover_tick = self._discovery_tick / 2
70 self._discovered_onus = [] # List of serial numbers
71
Chip Boling252c7772017-08-16 10:13:17 -050072 self._onus = {} # serial_number-base64 -> ONU (allowed list)
73 self._onu_by_id = {} # onu-id -> ONU
Chip Boling27275992017-09-22 15:17:04 -050074 self._next_onu_id = Onu.MIN_ONU_ID + 128
75 self._mcast_gem_ports = {} # VLAN -> GemPort
Chip Boling7294b252017-06-15 16:16:55 -050076
Chip Boling252c7772017-08-16 10:13:17 -050077 self._discovery_deferred = None # Specifically for ONU discovery
Chip Boling27275992017-09-22 15:17:04 -050078 self._active_los_alarms = set() # ONU-ID
79
80 # xPON configuration
Chip Boling7294b252017-06-15 16:16:55 -050081
Chip Boling252c7772017-08-16 10:13:17 -050082 self._xpon_name = None
Chip Boling27275992017-09-22 15:17:04 -050083 self._downstream_fec_enable = False
84 self._upstream_fec_enable = False
85 self._deployment_range = 25000
Chip Boling252c7772017-08-16 10:13:17 -050086 self._authentication_method = 'serial-number'
Chip Bolingfd1fd372017-12-20 13:34:12 -060087 self._mcast_aes = False
88 self._line_rate = 'down_10_up_10'
Chip Bolingab8863d2018-03-22 14:50:31 -050089 self._activation_method = 'autodiscovery'
Chip Boling3e3b1a92017-05-16 11:51:18 -050090
Chip Bolingfd1fd372017-12-20 13:34:12 -060091 # Statistics
Chip Bolingfd1fd372017-12-20 13:34:12 -060092 self.tx_bip_errors = 0
93
Chip Boling3e3b1a92017-05-16 11:51:18 -050094 def __str__(self):
Chip Boling252c7772017-08-16 10:13:17 -050095 return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
96 self._admin_state,
97 self._oper_status,
98 self.olt)
Chip Boling3e3b1a92017-05-16 11:51:18 -050099
100 def get_port(self):
101 """
102 Get the VOLTHA PORT object for this port
103 :return: VOLTHA Port object
104 """
Chip Boling7294b252017-06-15 16:16:55 -0500105 if self._port is None:
106 self._port = Port(port_no=self._port_no,
107 label=self._label,
108 type=Port.PON_OLT,
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500109 admin_state=self._admin_state,
110 oper_status=self._oper_status)
111
Chip Boling7294b252017-06-15 16:16:55 -0500112 return self._port
Chip Boling3e3b1a92017-05-16 11:51:18 -0500113
114 @property
Chip Boling252c7772017-08-16 10:13:17 -0500115 def xpon_name(self):
116 return self._xpon_name
117
118 @xpon_name.setter
119 def xpon_name(self, value):
Chip Boling61a12792017-10-02 13:23:27 -0500120 assert '/' not in value, "xPON names cannot have embedded forward slashes '/'"
Chip Boling252c7772017-08-16 10:13:17 -0500121 self._xpon_name = value
122
123 @property
Chip Boling3e3b1a92017-05-16 11:51:18 -0500124 def pon_id(self):
Chip Boling7294b252017-06-15 16:16:55 -0500125 return self._pon_id
126
127 @property
Chip Bolingfd1fd372017-12-20 13:34:12 -0600128 def onus(self):
129 """
130 Get a set of all ONUs. While the set is immutable, do not use this method
131 to get a collection that you will iterate through that my yield the CPU
132 such as inline callback. ONUs may be deleted at any time and they will
133 set some references to other objects to NULL during the 'delete' call.
134 Instead, get a list of ONU-IDs and iterate on these and call the 'onu'
135 method below (which will return 'None' if the ONU has been deleted.
136
137 :return: (frozenset) collection of ONU objects on this PON
138 """
139 return frozenset(self._onus.values())
140
141 @property
142 def onu_ids(self):
143 return frozenset(self._onu_by_id.keys())
144
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500145 def onu(self, onu_id):
146 return self._onu_by_id.get(onu_id)
147
Chip Boling7294b252017-06-15 16:16:55 -0500148 @property
Chip Bolingfd1fd372017-12-20 13:34:12 -0600149 def in_service_onus(self):
150 return len({onu.onu_id for onu in self.onus
151 if onu.onu_id not in self._active_los_alarms})
152
153 @property
154 def closest_onu_distance(self):
155 distance = -1
156 for onu in self.onus:
157 if onu.fiber_length < distance or distance == -1:
158 distance = onu.fiber_length
159 return distance
160
161 @property
Chip Boling27275992017-09-22 15:17:04 -0500162 def downstream_fec_enable(self):
163 return self._downstream_fec_enable
164
165 @downstream_fec_enable.setter
166 def downstream_fec_enable(self, value):
167 assert isinstance(value, bool), 'downstream FEC enabled is a boolean'
168
169 if self._downstream_fec_enable != value:
170 self._downstream_fec_enable = value
Chip Bolingab8863d2018-03-22 14:50:31 -0500171 if self.state == AdtnPort.State.RUNNING:
172 self.deferred = self._set_pon_config("downstream-fec-enable", value)
Chip Boling27275992017-09-22 15:17:04 -0500173
174 @property
175 def upstream_fec_enable(self):
176 return self._upstream_fec_enable
177
178 @upstream_fec_enable.setter
179 def upstream_fec_enable(self, value):
180 assert isinstance(value, bool), 'upstream FEC enabled is a boolean'
Chip Boling27275992017-09-22 15:17:04 -0500181 if self._upstream_fec_enable != value:
182 self._upstream_fec_enable = value
Chip Bolingab8863d2018-03-22 14:50:31 -0500183 if self.state == AdtnPort.State.RUNNING:
184 self.deferred = self._set_pon_config("upstream-fec-enable", value)
Chip Boling27275992017-09-22 15:17:04 -0500185
186 @property
Chip Bolingfd1fd372017-12-20 13:34:12 -0600187 def any_upstream_fec_enabled(self):
188 for onu in self.onus:
189 if onu.upstream_fec_enable and onu.enabled:
190 return True
191 return False
192
193 @property
194 def mcast_aes(self):
195 return self._mcast_aes
196
197 @mcast_aes.setter
198 def mcast_aes(self, value):
199 assert isinstance(value, bool), 'MCAST AES is a boolean'
200 if self._mcast_aes != value:
201 self._mcast_aes = value
Chip Bolingab8863d2018-03-22 14:50:31 -0500202 if self.state == AdtnPort.State.RUNNING:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600203 pass # TODO
204
205 @property
206 def line_rate(self):
207 return self._line_rate
208
209 @line_rate.setter
210 def line_rate(self, value):
211 assert isinstance(value, (str, unicode)), 'Line Rate is a string'
212 # TODO cast to enum
213 if self._line_rate != value:
214 self._line_rate = value
Chip Bolingab8863d2018-03-22 14:50:31 -0500215 if self.state == AdtnPort.State.RUNNING:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600216 pass # TODO
217
218 @property
Chip Boling27275992017-09-22 15:17:04 -0500219 def deployment_range(self):
220 """Maximum deployment range (in meters)"""
221 return self._deployment_range
222
223 @deployment_range.setter
224 def deployment_range(self, value):
225 """Maximum deployment range (in meters)"""
226 if not 0 <= value <= PonPort.MAX_DEPLOYMENT_RANGE:
227 raise ValueError('Deployment range should be 0..{} meters'.
228 format(PonPort.MAX_DEPLOYMENT_RANGE))
229 if self._deployment_range != value:
230 self._deployment_range = value
Chip Bolingab8863d2018-03-22 14:50:31 -0500231 if self.state == AdtnPort.State.RUNNING:
232 self.deferred = self._set_pon_config("deployment-range", value)
Chip Boling27275992017-09-22 15:17:04 -0500233
234 @property
Chip Boling252c7772017-08-16 10:13:17 -0500235 def discovery_tick(self):
236 return self._discovery_tick * 10
237
238 @discovery_tick.setter
239 def discovery_tick(self, value):
240 if value < 0:
241 raise ValueError("Polling interval must be >= 0")
242
243 if self.discovery_tick != value:
244 self._discovery_tick = value / 10
245
Chip Boling27275992017-09-22 15:17:04 -0500246 try:
247 if self._discovery_deferred is not None and \
248 not self._discovery_deferred.called:
249 self._discovery_deferred.cancel()
250 except:
251 pass
252 self._discovery_deferred = None
Chip Boling252c7772017-08-16 10:13:17 -0500253
254 if self._discovery_tick > 0:
255 self._discovery_deferred = reactor.callLater(self._discovery_tick,
256 self._discover_onus)
257
258 @property
259 def activation_method(self):
260 return self._activation_method
261
262 @activation_method.setter
263 def activation_method(self, value):
264 value = value.lower()
265 if value not in PonPort._SUPPORTED_ACTIVATION_METHODS:
266 raise ValueError('Invalid ONU activation method')
267 self._activation_method = value
268
269 @property
270 def authentication_method(self):
271 return self._authentication_method
272
273 @authentication_method.setter
274 def authentication_method(self, value):
275 value = value.lower()
276 if value not in PonPort._SUPPORTED_AUTHENTICATION_METHODS:
277 raise ValueError('Invalid ONU authentication method')
278 self._authentication_method = value
279
Chip Bolingab8863d2018-03-22 14:50:31 -0500280 def cancel_deferred(self):
281 super(PonPort, self).cancel_deferred()
Chip Boling7294b252017-06-15 16:16:55 -0500282
Chip Bolingab8863d2018-03-22 14:50:31 -0500283 d, self._discovery_deferred = self._discovery_deferred, None
Chip Boling3e3b1a92017-05-16 11:51:18 -0500284
Chip Bolingab8863d2018-03-22 14:50:31 -0500285 try:
286 if d is not None and not d.called:
287 d.cancel()
288 except Exception as e:
289 pass
Chip Boling7294b252017-06-15 16:16:55 -0500290
291 def _update_adapter_agent(self):
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500292 """
293 Update the port status and state in the core
294 """
295 self.log.debug('update-adapter-agent', admin_state=self._admin_state,
Chip Boling3971d242018-06-06 10:26:34 -0500296 oper_status=self._oper_status)
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500297
298 # because the core does not provide methods for updating admin
299 # and oper status per port, we need to copy any existing port
300 # info so that we don't wipe out the peers
301 if self._port is not None:
302 agent_ports = self.adapter_agent.get_ports(self.olt.device_id, Port.PON_OLT)
303
304 agent_port = next((ap for ap in agent_ports if ap.port_no == self._port_no), None)
305
306 # copy current Port info
307 if agent_port is not None:
Chip Boling3971d242018-06-06 10:26:34 -0500308 self._port = agent_port
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500309
310 # set new states
311 self._port.admin_state = self._admin_state
312 self._port.oper_status = self._oper_status
313
314 # adapter_agent add_port also does an update of existing port
315 self.adapter_agent.add_port(self.olt.device_id, self.get_port())
Chip Boling7294b252017-06-15 16:16:55 -0500316
Chip Boling7294b252017-06-15 16:16:55 -0500317 @inlineCallbacks
Chip Bolingab8863d2018-03-22 14:50:31 -0500318 def finish_startup(self):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500319 """
Chip Boling7294b252017-06-15 16:16:55 -0500320 Do all startup offline since REST may fail
Chip Boling3e3b1a92017-05-16 11:51:18 -0500321 """
Chip Bolingab8863d2018-03-22 14:50:31 -0500322 if self.state != AdtnPort.State.INITIAL:
Chip Boling7294b252017-06-15 16:16:55 -0500323 returnValue('Done')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500324
Chip Boling252c7772017-08-16 10:13:17 -0500325 self.log.debug('final-startup')
Chip Bolingab8863d2018-03-22 14:50:31 -0500326 results = None
Chip Boling5561d552017-07-07 15:11:26 -0500327
Chip Boling27275992017-09-22 15:17:04 -0500328 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500329 self.deferred = self._get_pon_config()
330 results = yield self.deferred
Chip Boling27275992017-09-22 15:17:04 -0500331
332 except Exception as e:
333 self.log.exception('initial-GET', e=e)
Chip Bolingab8863d2018-03-22 14:50:31 -0500334 self.deferred = reactor.callLater(5, self.finish_startup)
335 returnValue(self.deferred)
Chip Boling27275992017-09-22 15:17:04 -0500336
337 # Load config from hardware
338
339 enabled = results.get('enabled', False)
340 downstream_fec_enable = results.get('downstream-fec-enable', False)
341 upstream_fec_enable = results.get('upstream-fec-enable', False)
342 deployment_range = results.get('deployment-range', 25000)
343 self._in_sync = True
344
345 if enabled != self._enabled:
Chip Boling7294b252017-06-15 16:16:55 -0500346 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500347 self.deferred = self._set_pon_config("enabled", True)
348 yield self.deferred
Chip Boling3e3b1a92017-05-16 11:51:18 -0500349
Chip Boling7294b252017-06-15 16:16:55 -0500350 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -0500351 self.log.exception('final-startup-enable', e=e)
Chip Bolingab8863d2018-03-22 14:50:31 -0500352 self.deferred = reactor.callLater(3, self.finish_startup)
353 returnValue(self.deferred)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500354
Chip Boling27275992017-09-22 15:17:04 -0500355 if downstream_fec_enable != self._downstream_fec_enable:
Chip Boling7294b252017-06-15 16:16:55 -0500356 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500357 self.deferred = self._set_pon_config("downstream-fec-enable",
358 self._downstream_fec_enable)
359 yield self.deferred
Chip Boling3e3b1a92017-05-16 11:51:18 -0500360
Chip Boling7294b252017-06-15 16:16:55 -0500361 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500362 self.log.warning('final-startup-downstream-FEC', e=e)
363 self._in_sync = False
364 # Non-fatal. May have failed due to no SFQ in slot
Chip Boling7294b252017-06-15 16:16:55 -0500365
Chip Boling27275992017-09-22 15:17:04 -0500366 if upstream_fec_enable != self._upstream_fec_enable:
Chip Boling7294b252017-06-15 16:16:55 -0500367 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500368 self.deferred = self._set_pon_config("upstream-fec-enable",
369 self._upstream_fec_enable)
370 yield self.deferred
Chip Boling7294b252017-06-15 16:16:55 -0500371
372 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500373 self.log.warning('final-startup-upstream-FEC', e=e)
374 self._in_sync = False
375 # Non-fatal. May have failed due to no SFQ in slot
Chip Boling7294b252017-06-15 16:16:55 -0500376
Chip Boling27275992017-09-22 15:17:04 -0500377 if deployment_range != self._deployment_range:
378 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500379 self.deferred = self._set_pon_config("deployment-range",
380 self._deployment_range)
381 yield self.deferred
Chip Boling7294b252017-06-15 16:16:55 -0500382
Chip Boling27275992017-09-22 15:17:04 -0500383 except Exception as e:
384 self.log.warning('final-startup-deployment-range', e=e)
385 self._in_sync = False
386 # Non-fatal. May have failed due to no SFQ in slot
Chip Boling7294b252017-06-15 16:16:55 -0500387
Chip Boling27275992017-09-22 15:17:04 -0500388 if len(self._onus) > 0:
389 dl = []
Chip Bolingfd1fd372017-12-20 13:34:12 -0600390 for onu_id in self.onu_ids:
391 onu = self.onu(onu_id)
392 if onu is not None:
393 dl.append(onu.restart())
Chip Boling27275992017-09-22 15:17:04 -0500394 yield defer.gatherResults(dl, consumeErrors=True)
Chip Boling7294b252017-06-15 16:16:55 -0500395
Chip Boling27275992017-09-22 15:17:04 -0500396 # Begin to ONU discovery and hardware sync
Chip Boling3e3b1a92017-05-16 11:51:18 -0500397
Chip Boling27275992017-09-22 15:17:04 -0500398 self._discovery_deferred = reactor.callLater(5, self._discover_onus)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500399
Chip Bolingab8863d2018-03-22 14:50:31 -0500400 # If here, initial settings were successfully written to hardware
401
402 super(PonPort, self).finish_startup()
Chip Boling27275992017-09-22 15:17:04 -0500403 returnValue('Enabled')
Chip Boling7294b252017-06-15 16:16:55 -0500404
Chip Bolingab8863d2018-03-22 14:50:31 -0500405 def finish_stop(self):
Chip Bolingfd1fd372017-12-20 13:34:12 -0600406 # Remove all existing ONUs. They will need to be re-discovered
Chip Bolingab8863d2018-03-22 14:50:31 -0500407 dl = []
Chip Bolingfd1fd372017-12-20 13:34:12 -0600408 onu_ids = frozenset(self._onu_by_id.keys())
409 for onu_id in onu_ids:
410 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500411 dl.append(self.delete_onu(onu_id))
412
Chip Bolingfd1fd372017-12-20 13:34:12 -0600413 except Exception as e:
414 self.log.exception('onu-cleanup', onu_id=onu_id, e=e)
415
Chip Bolingab8863d2018-03-22 14:50:31 -0500416 dl.append(self._set_pon_config("enabled", False))
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500417
Chip Bolingab8863d2018-03-22 14:50:31 -0500418 return defer.gatherResults(dl, consumeErrors=True)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500419
420 @inlineCallbacks
421 def reset(self):
Chip Boling7294b252017-06-15 16:16:55 -0500422 """
423 Set the PON Port to a known good state on initial port startup. Actual
424 PON 'Start' is done elsewhere
425 """
Chip Bolingab8863d2018-03-22 14:50:31 -0500426 initial_port_state = AdminState.DISABLED
Chip Boling27275992017-09-22 15:17:04 -0500427 self.log.info('reset', initial_state=initial_port_state)
Chip Boling7294b252017-06-15 16:16:55 -0500428
Chip Boling252c7772017-08-16 10:13:17 -0500429 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500430 self.deferred = self._get_pon_config()
431 results = yield self.deferred
Chip Boling27275992017-09-22 15:17:04 -0500432 enabled = results.get('enabled', False)
Chip Boling252c7772017-08-16 10:13:17 -0500433
434 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500435 self.log.exception('get-config', e=e)
436 enabled = False
Chip Boling252c7772017-08-16 10:13:17 -0500437
Chip Boling27275992017-09-22 15:17:04 -0500438 enable = initial_port_state == AdminState.ENABLED
Chip Boling252c7772017-08-16 10:13:17 -0500439
Chip Boling27275992017-09-22 15:17:04 -0500440 if enable != enabled:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500441 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500442 self.deferred = yield self._set_pon_config("enabled", enable)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500443 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500444 self.log.exception('reset-enabled', e=e, enabled=enabled)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500445
Chip Boling27275992017-09-22 15:17:04 -0500446 # TODO: Move to 'set_pon_config' method and also make sure GRPC/Port is ok
447 self._admin_state = AdminState.ENABLED if enable else AdminState.DISABLED
Chip Boling3e3b1a92017-05-16 11:51:18 -0500448
Chip Boling252c7772017-08-16 10:13:17 -0500449 try:
Chip Boling27275992017-09-22 15:17:04 -0500450 # Walk the provisioned ONU list and disable any exiting ONUs
451 results = yield self._get_onu_config()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500452
Chip Boling252c7772017-08-16 10:13:17 -0500453 if isinstance(results, list) and len(results) > 0:
454 onu_configs = OltConfig.Pon.Onu.decode(results)
Chip Boling27275992017-09-22 15:17:04 -0500455 dl = []
Chip Boling252c7772017-08-16 10:13:17 -0500456 for onu_id in onu_configs.iterkeys():
Chip Boling27275992017-09-22 15:17:04 -0500457 dl.append(self.delete_onu(onu_id))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500458
Chip Boling27275992017-09-22 15:17:04 -0500459 try:
460 if len(dl) > 0:
461 yield defer.gatherResults(dl, consumeErrors=True)
462
463 except Exception as e:
Chip Bolingab8863d2018-03-22 14:50:31 -0500464 self.log.exception('rest-ONU-delete', e=e)
Chip Boling27275992017-09-22 15:17:04 -0500465 pass # Non-fatal
Chip Boling252c7772017-08-16 10:13:17 -0500466
467 except Exception as e:
468 self.log.exception('onu-delete', e=e)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500469
Chip Boling5561d552017-07-07 15:11:26 -0500470 returnValue('Reset complete')
471
Chip Bolingbb15b512018-06-01 11:39:58 -0500472 def gem_ids(self, logical_port, untagged_gem, multicast_gems=False):
Chip Boling252c7772017-08-16 10:13:17 -0500473 """
474 Get all GEM Port IDs used on a given PON
475
Chip Bolingab8863d2018-03-22 14:50:31 -0500476 :param logical_port: (int) Logical port umber of ONU. None if for all ONUs
477 on PON, if Multicast, VID for Multicast, or None for all
Chip Boling27275992017-09-22 15:17:04 -0500478 Multicast GEMPorts
Chip Bolingab8863d2018-03-22 14:50:31 -0500479 :param untagged_gem: (boolean) Select from special purpose untagged GEM Port
Chip Boling27275992017-09-22 15:17:04 -0500480 :param multicast_gems: (boolean) Select from available Multicast GEM Ports
481 :return: (dict) data_gem -> key -> onu-id, value -> tuple(sorted list of GEM Port IDs, onu_vid)
482 mcast_gem-> key -> mcast-vid, value -> GEM Port IDs
Chip Boling252c7772017-08-16 10:13:17 -0500483 """
484 gem_ids = {}
Chip Boling27275992017-09-22 15:17:04 -0500485
486 if multicast_gems:
487 # Multicast GEMs belong to the PON, but we may need to register them on
488 # all ONUs. Rework when BBF MCAST Gems are supported
Chip Bolingab8863d2018-03-22 14:50:31 -0500489 for vlan, gem_port in self._mcast_gem_ports.iteritems(): # TODO: redo logic
490 if logical_port is None or (logical_port == vlan and logical_port in self.olt.multicast_vlans):
Chip Boling27275992017-09-22 15:17:04 -0500491 gem_ids[vlan] = ([gem_port.gem_id], None)
492 else:
493 for onu_id, onu in self._onu_by_id.iteritems():
Chip Bolingab8863d2018-03-22 14:50:31 -0500494 if logical_port is None or logical_port == onu.logical_port:
Chip Bolingbb15b512018-06-01 11:39:58 -0500495 gem_ids[onu_id] = (onu.gem_ids(untagged_gem),
Chip Bolingab8863d2018-03-22 14:50:31 -0500496 onu.onu_vid if not untagged_gem
497 else self.olt.untagged_vlan)
Chip Boling252c7772017-08-16 10:13:17 -0500498 return gem_ids
499
Chip Boling27275992017-09-22 15:17:04 -0500500 def _get_pon_config(self):
Chip Boling7294b252017-06-15 16:16:55 -0500501 uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id)
502 name = 'pon-get-config-{}'.format(self._pon_id)
503 return self._parent.rest_client.request('GET', uri, name=name)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500504
Chip Boling27275992017-09-22 15:17:04 -0500505 def _get_onu_config(self, onu_id=None):
Chip Boling252c7772017-08-16 10:13:17 -0500506 if onu_id is None:
507 uri = AdtranOltHandler.GPON_ONU_CONFIG_LIST_URI.format(self._pon_id)
508 else:
509 uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, onu_id)
510
Chip Boling7294b252017-06-15 16:16:55 -0500511 name = 'pon-get-onu_config-{}-{}'.format(self._pon_id, onu_id)
512 return self._parent.rest_client.request('GET', uri, name=name)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500513
Chip Boling27275992017-09-22 15:17:04 -0500514 def _set_pon_config(self, leaf, value):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500515 data = json.dumps({leaf: value})
Chip Boling7294b252017-06-15 16:16:55 -0500516 uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id)
517 name = 'pon-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value))
518 return self._parent.rest_client.request('PATCH', uri, data=data, name=name)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500519
Chip Boling252c7772017-08-16 10:13:17 -0500520 def _discover_onus(self):
Chip Boling27275992017-09-22 15:17:04 -0500521 self.log.debug('discovery', state=self._admin_state, in_sync=self._in_sync)
Chip Boling7294b252017-06-15 16:16:55 -0500522 if self._admin_state == AdminState.ENABLED:
Chip Boling27275992017-09-22 15:17:04 -0500523 if self._in_sync:
524 data = json.dumps({'pon-id': self._pon_id})
525 uri = AdtranOltHandler.GPON_PON_DISCOVER_ONU
526 name = 'pon-discover-onu-{}'.format(self._pon_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500527
Chip Boling27275992017-09-22 15:17:04 -0500528 self._discovery_deferred = self._parent.rest_client.request('POST', uri, data, name=name)
529 self._discovery_deferred.addBoth(self._onu_discovery_init_complete)
530 else:
531 self.discovery_deferred = reactor.callLater(0,
532 self._onu_discovery_init_complete,
533 None)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500534
Chip Bolingab8863d2018-03-22 14:50:31 -0500535 def _onu_discovery_init_complete(self, _result):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500536 """
537 This method is called after the REST POST to request ONU discovery is
538 completed. The results (body) of the post is always empty / 204 NO CONTENT
539 """
Chip Boling7294b252017-06-15 16:16:55 -0500540 delay = self._no_onu_discover_tick if len(self._onus) == 0 else self._discovery_tick
Chip Boling3e3b1a92017-05-16 11:51:18 -0500541 delay += random.uniform(-delay / 10, delay / 10)
Chip Boling252c7772017-08-16 10:13:17 -0500542 self._discovery_deferred = reactor.callLater(delay, self._discover_onus)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500543
Chip Bolingab8863d2018-03-22 14:50:31 -0500544 def sync_hardware(self):
545 if self.state == AdtnPort.State.RUNNING or self.state == AdtnPort.State.STOPPED:
Chip Boling27275992017-09-22 15:17:04 -0500546 def read_config(results):
547 self.log.debug('read-config', results=results)
548 config = OltConfig.Pon.decode([results])
549 assert self.pon_id in config, 'sync-pon-not-found-{}'.format(self.pon_id)
550 config = config[self.pon_id]
551 self._in_sync = True
552
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500553 dl = []
Chip Boling27275992017-09-22 15:17:04 -0500554
555 if self.enabled != config.enabled:
556 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500557 self._expedite_sync = True
Chip Boling27275992017-09-22 15:17:04 -0500558 dl.append(self._set_pon_config("enabled", self.enabled))
559
Chip Bolingab8863d2018-03-22 14:50:31 -0500560 elif self.state == AdtnPort.State.RUNNING:
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500561 if self.deployment_range != config.deployment_range:
562 self._in_sync = False
563 self._expedite_sync = True
564 dl.append(self._set_pon_config("deployment-range",
565 self.deployment_range))
566
Chip Boling27275992017-09-22 15:17:04 -0500567 if self.downstream_fec_enable != config.downstream_fec_enable:
568 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500569 self._expedite_sync = True
Chip Boling27275992017-09-22 15:17:04 -0500570 dl.append(self._set_pon_config("downstream-fec-enable",
571 self.downstream_fec_enable))
572
573 if self.upstream_fec_enable != config.upstream_fec_enable:
574 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500575 self._expedite_sync = True
Chip Boling27275992017-09-22 15:17:04 -0500576 dl.append(self._set_pon_config("upstream-fec-enable",
577 self.upstream_fec_enable))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600578 defer.gatherResults(dl, consumeErrors=True)
579 return config.onus
Chip Boling27275992017-09-22 15:17:04 -0500580
Chip Bolingfd1fd372017-12-20 13:34:12 -0600581 def sync_onus(hw_onus):
Chip Bolingab8863d2018-03-22 14:50:31 -0500582 if self.state == AdtnPort.State.RUNNING:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600583 self.log.debug('sync-pon-onu-results', config=hw_onus)
Chip Boling27275992017-09-22 15:17:04 -0500584
585 # ONU's have their own sync task, extra (should be deleted) are
586 # handled here. Missing are handled by normal discovery mechanisms.
587
Chip Bolingfd1fd372017-12-20 13:34:12 -0600588 hw_onu_ids = frozenset(hw_onus.keys())
Chip Boling27275992017-09-22 15:17:04 -0500589 my_onu_ids = frozenset(self._onu_by_id.keys())
590
591 extra_onus = hw_onu_ids - my_onu_ids
592 dl = [self.delete_onu(onu_id) for onu_id in extra_onus]
593
Chip Boling27275992017-09-22 15:17:04 -0500594 return defer.gatherResults(dl, consumeErrors=True)
595
596 def failure(reason, what):
597 self.log.error('hardware-sync-{}-failed'.format(what), reason=reason)
598 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500599 self._expedite_sync = False
Chip Boling27275992017-09-22 15:17:04 -0500600
601 def reschedule(_):
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500602 # Speed up sequential resync a limited number of times if out of sync.
603
Chip Bolingab8863d2018-03-22 14:50:31 -0500604 delay = self.sync_tick
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500605
606 if self._expedite_sync:
607 self._expedite_count += 1
608 if self._expedite_count < 5:
609 delay = 1
610 else:
611 self._expedite_count = 0
612
Chip Boling27275992017-09-22 15:17:04 -0500613 delay += random.uniform(-delay / 10, delay / 10)
Chip Bolingab8863d2018-03-22 14:50:31 -0500614 self.sync_deferred = reactor.callLater(delay, self.sync_hardware)
Chip Boling27275992017-09-22 15:17:04 -0500615
Chip Bolingab8863d2018-03-22 14:50:31 -0500616 self.sync_deferred = self._get_pon_config()
617 self.sync_deferred.addCallbacks(read_config, failure, errbackArgs=['get-config'])
618 self.sync_deferred.addCallbacks(sync_onus, failure, errbackArgs=['pon-sync'])
619 self.sync_deferred.addBoth(reschedule)
Chip Boling27275992017-09-22 15:17:04 -0500620
Chip Boling3e3b1a92017-05-16 11:51:18 -0500621 def process_status_poll(self, status):
622 """
623 Process PON status poll request
624
625 :param status: (OltState.Pon object) results from RESTCONF GET
626 """
Chip Boling252c7772017-08-16 10:13:17 -0500627 self.log.debug('process-status-poll', status=status)
Chip Boling7294b252017-06-15 16:16:55 -0500628
629 if self._admin_state != AdminState.ENABLED:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500630 return
631
Chip Bolingfd1fd372017-12-20 13:34:12 -0600632 # Process LOS list
633 self._process_los_alarms(frozenset(status.ont_los))
634
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500635 # Get new/missing from the discovered ONU leaf. Stale ONUs from previous
636 # configs are now cleaned up during h/w re-sync/reflow.
Chip Boling3e3b1a92017-05-16 11:51:18 -0500637
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500638 new, rediscovered_onus = self._process_status_onu_discovered_list(status.discovered_onu)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500639
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500640 # Process newly discovered ONU list and rediscovered ONUs
Chip Boling3e3b1a92017-05-16 11:51:18 -0500641
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500642 for serial_number in new | rediscovered_onus:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500643 reactor.callLater(0, self.add_onu, serial_number, status)
644
Chip Bolingfd1fd372017-12-20 13:34:12 -0600645 # PON Statistics
646 self._process_statistics(status)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500647
Chip Boling27275992017-09-22 15:17:04 -0500648 # Process ONU info. Note that newly added ONUs will not be processed
649 # until the next pass
Chip Boling27275992017-09-22 15:17:04 -0500650 self._update_onu_status(status.onus)
651
Chip Bolingfd1fd372017-12-20 13:34:12 -0600652 # Process GEM Port information
653 self._update_gem_status(status.gems)
654
655 def _handle_discovered_onu(self, child_device, ind_info):
656 pon_id = ind_info['_pon_id']
657 olt_id = ind_info['_olt_id']
658
659 if ind_info['_sub_group_type'] == 'onu_discovery':
660 self.log.info('Activation-is-in-progress', olt_id=olt_id,
661 pon_ni=pon_id, onu_data=ind_info,
662 onu_id=child_device.proxy_address.onu_id)
663
664 elif ind_info['_sub_group_type'] == 'sub_term_indication':
665 self.log.info('ONU-activation-is-completed', olt_id=olt_id,
666 pon_ni=pon_id, onu_data=ind_info)
667
668 msg = {'proxy_address': child_device.proxy_address,
669 'event': 'activation-completed', 'event_data': ind_info}
670
671 # Send the event message to the ONU adapter
672 self.adapter_agent.publish_inter_adapter_message(child_device.id,
673 msg)
674 if ind_info['activation_successful'] is True:
675 for key, v_ont_ani in dict(): # self.v_ont_anis.items():
676 if v_ont_ani.v_ont_ani.data.onu_id == \
677 child_device.proxy_address.onu_id:
678 for tcont_key, tcont in v_ont_ani.tconts.items():
679 owner_info = dict()
680 # To-Do: Right Now use alloc_id as schduler ID. Need to
681 # find way to generate uninqe number.
682 id = tcont.alloc_id
683 owner_info['type'] = 'agg_port'
684 owner_info['intf_id'] = \
685 child_device.proxy_address.channel_id
686 owner_info['onu_id'] = \
687 child_device.proxy_address.onu_id
688 owner_info['alloc_id'] = tcont.alloc_id
689 # self.bal.create_scheduler(id, 'upstream', owner_info, 8)
690 else:
691 self.log.info('Invalid-ONU-event', olt_id=olt_id,
692 pon_ni=ind_info['_pon_id'], onu_data=ind_info)
693
694 def _process_statistics(self, status):
695 self.rx_packets = status.rx_packets
696 self.rx_bytes = status.rx_bytes
697 self.tx_packets = status.tx_packets
698 self.tx_bytes = status.tx_bytes
699 self.tx_bip_errors = status.tx_bip_errors
700
Chip Boling27275992017-09-22 15:17:04 -0500701 def _update_onu_status(self, onus):
702 """
703 Process ONU status for this PON
704 :param onus: (dict) onu_id: ONU State
705 """
706 for onu_id, onu_status in onus.iteritems():
707 if onu_id in self._onu_by_id:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600708 onu = self._onu_by_id[onu_id]
709 onu.rssi = onu_status.rssi
710 onu.equalization_delay = onu_status.equalization_delay
711 onu.equalization_delay = onu_status.equalization_delay
712 onu.fiber_length = onu_status.fiber_length
Chip Bolingbb15b512018-06-01 11:39:58 -0500713 onu.password = onu_status.reported_password
Chip Bolingfd1fd372017-12-20 13:34:12 -0600714
715 def _update_gem_status(self, gems):
716 for gem_id, gem_status in gems.iteritems():
717 onu = self._onu_by_id.get(gem_status.onu_id)
718 if onu is not None:
719 gem_port = onu.gem_port(gem_status.gem_id)
720 if gem_port is not None:
721 gem_port.rx_packets = gem_status.rx_packets
722 gem_port.rx_bytes = gem_status.rx_bytes
723 gem_port.tx_packets = gem_status.tx_packets
724 gem_port.tx_bytes = gem_status.tx_bytes
Chip Boling27275992017-09-22 15:17:04 -0500725
726 def _process_los_alarms(self, ont_los):
727 """
728 Walk current LOS and set/clear LOS as appropriate
729 :param ont_los: (frozenset) ONU IDs of ONUs in LOS alarm state
730 """
731 cleared_alarms = self._active_los_alarms - ont_los
732 new_alarms = ont_los - self._active_los_alarms
733
Chip Boling27275992017-09-22 15:17:04 -0500734 if len(cleared_alarms) > 0 or len(new_alarms) > 0:
735 self.log.info('onu-los', cleared=cleared_alarms, new=new_alarms)
736
737 for onu_id in cleared_alarms:
Chip Boling27275992017-09-22 15:17:04 -0500738 self._active_los_alarms.remove(onu_id)
Chip Bolingbffef5e2018-08-07 14:53:12 -0500739 OnuLosAlarm(self.olt.alarms, onu_id).clear_alarm()
Chip Boling27275992017-09-22 15:17:04 -0500740
741 for onu_id in new_alarms:
742 self._active_los_alarms.add(onu_id)
Chip Bolingbffef5e2018-08-07 14:53:12 -0500743 OnuLosAlarm(self.olt.alarms, onu_id).raise_alarm()
Chip Bolingfd1fd372017-12-20 13:34:12 -0600744 self.delete_onu(onu_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500745
Chip Boling3e3b1a92017-05-16 11:51:18 -0500746 def _process_status_onu_discovered_list(self, discovered_onus):
747 """
Chip Bolingae298012017-08-28 08:55:17 -0500748 Look for new ONUs
Chip Boling3e3b1a92017-05-16 11:51:18 -0500749
750 :param discovered_onus: (frozenset) Set of ONUs currently discovered
751 """
Chip Boling252c7772017-08-16 10:13:17 -0500752 self.log.debug('discovered-ONUs', list=discovered_onus)
753
754 # Only request discovery if activation is auto-discovery or auto-activate
Chip Bolingab8863d2018-03-22 14:50:31 -0500755 continue_discovery = ['autodiscovery'] # , 'autoactivate']
Chip Boling252c7772017-08-16 10:13:17 -0500756
757 if self._activation_method not in continue_discovery:
758 return set(), set()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500759
Chip Boling7294b252017-06-15 16:16:55 -0500760 my_onus = frozenset(self._onus.keys())
Chip Boling3e3b1a92017-05-16 11:51:18 -0500761
762 new_onus = discovered_onus - my_onus
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500763 rediscovered_onus = my_onus & discovered_onus
Chip Boling3e3b1a92017-05-16 11:51:18 -0500764
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500765 return new_onus, rediscovered_onus
Chip Boling3e3b1a92017-05-16 11:51:18 -0500766
Chip Boling252c7772017-08-16 10:13:17 -0500767 def _get_onu_info(self, serial_number):
768 """
769 Parse through available xPON information for ONU configuration settings
770 :param serial_number: (string) Decoded (not base64) serial number string
771 :return: (dict) onu config data or None on lookup failure
772 """
773 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500774 if self.activation_method == "autodiscovery":
Chip Boling252c7772017-08-16 10:13:17 -0500775 if self.authentication_method == 'serial-number':
776 gpon_info = self.olt.get_xpon_info(self.pon_id)
777
778 try:
Chip Boling27275992017-09-22 15:17:04 -0500779 # TODO: Change iteration to itervalues below
Chip Bolingfd1fd372017-12-20 13:34:12 -0600780 vont_info = next(info for _, info in gpon_info['vont-anis'].items()
Chip Boling252c7772017-08-16 10:13:17 -0500781 if info.get('expected-serial-number') == serial_number)
782
Chip Bolingbb15b512018-06-01 11:39:58 -0500783 # ont_info = next(info for _, info in gpon_info['ont-anis'].items()
784 # if info.get('name') == vont_info['name'])
Chip Bolingfd1fd372017-12-20 13:34:12 -0600785
786 vont_ani = vont_info['data']
Chip Boling252c7772017-08-16 10:13:17 -0500787 onu_id = vont_info['onu-id']
788 enabled = vont_info['enabled']
789 channel_speed = vont_info['upstream-channel-speed']
Chip Bolingbb15b512018-06-01 11:39:58 -0500790 xpon_name = vont_info['name']
791 upstream_fec_enabled = True # TODO: ont_info.get('upstream-fec', False)
Chip Boling252c7772017-08-16 10:13:17 -0500792
Chip Bolingae298012017-08-28 08:55:17 -0500793 tconts = {key: val for key, val in gpon_info['tconts'].iteritems()
794 if val.vont_ani == vont_info['name']}
Chip Bolingae298012017-08-28 08:55:17 -0500795
796 gem_ports = {key: val for key, val in gpon_info['gem-ports'].iteritems()
Chip Bolingfd1fd372017-12-20 13:34:12 -0600797 if val.tcont_ref in tconts.keys()}
Chip Bolingae298012017-08-28 08:55:17 -0500798
Chip Bolingab8863d2018-03-22 14:50:31 -0500799 venet = next((val for val in gpon_info['v-enets'].itervalues()
800 if val['vont-ani'] == vont_info['name']), None)
801 # TODO: need to handle case where ont_ani, gems, venets, tconts are assigned
802 # after activation is started. only vont-ani needs to be set to get here
803
Chip Boling252c7772017-08-16 10:13:17 -0500804 except StopIteration:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600805 # Can happen if vont-ani or ont-ani has not yet been configured
806 self.log.debug('no-vont-or-ont')
807 return None
808
809 except Exception as e:
810 self.log.exception('autodiscovery', e=e)
811 raise
Chip Boling252c7772017-08-16 10:13:17 -0500812 else:
Chip Bolingff3087f2017-10-12 09:26:29 -0500813 self.log.debug('not-serial-number-authentication')
Chip Boling252c7772017-08-16 10:13:17 -0500814 return None
815 else:
Chip Bolingff3087f2017-10-12 09:26:29 -0500816 self.log.debug('not-auto-discovery')
Chip Boling252c7772017-08-16 10:13:17 -0500817 return None
818
819 onu_info = {
Chip Boling27275992017-09-22 15:17:04 -0500820 'device-id': self.olt.device_id,
Chip Boling252c7772017-08-16 10:13:17 -0500821 'serial-number': serial_number,
Chip Bolingfd1fd372017-12-20 13:34:12 -0600822 'xpon-name': xpon_name,
Chip Boling252c7772017-08-16 10:13:17 -0500823 'pon': self,
824 'onu-id': onu_id,
825 'enabled': enabled,
826 'upstream-channel-speed': channel_speed,
Chip Bolingfd1fd372017-12-20 13:34:12 -0600827 'upstream-fec': upstream_fec_enabled,
Chip Boling252c7772017-08-16 10:13:17 -0500828 'password': Onu.DEFAULT_PASSWORD,
Chip Bolingae298012017-08-28 08:55:17 -0500829 't-conts': tconts,
830 'gem-ports': gem_ports,
Chip Bolingab8863d2018-03-22 14:50:31 -0500831 'onu-vid': self.olt.get_onu_vid(onu_id),
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500832 'channel-id': self.olt.get_channel_id(self._pon_id, onu_id),
Chip Bolingab8863d2018-03-22 14:50:31 -0500833 'vont-ani': vont_ani,
834 'venet': venet
Chip Boling252c7772017-08-16 10:13:17 -0500835 }
Chip Bolingae298012017-08-28 08:55:17 -0500836 # Hold off ONU activation until at least one GEM Port is defined.
Chip Bolingff3087f2017-10-12 09:26:29 -0500837 self.log.debug('onu-info', gem_ports=gem_ports)
Chip Bolingae298012017-08-28 08:55:17 -0500838
Chip Boling3971d242018-06-06 10:26:34 -0500839 # return onu_info
840 return onu_info if len(gem_ports) > 0 and venet is not None else None
Chip Boling252c7772017-08-16 10:13:17 -0500841
842 except Exception as e:
843 self.log.exception('get-onu-info', e=e)
844 return None
845
Chip Boling3e3b1a92017-05-16 11:51:18 -0500846 @inlineCallbacks
Chip Bolingfd1fd372017-12-20 13:34:12 -0600847 def add_onu(self, serial_number_64, status):
848 serial_number = Onu.serial_number_to_string(serial_number_64)
849 self.log.info('add-onu', serial_number=serial_number,
850 serial_number_64=serial_number_64, status=status)
851 onu_info = self._get_onu_info(serial_number)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500852
Chip Bolingff3087f2017-10-12 09:26:29 -0500853 if onu_info is None:
Chip Bolingbffef5e2018-08-07 14:53:12 -0500854 self.log.info('onu-lookup-failure', serial_number=serial_number,
855 serial_number_64=serial_number_64)
856 OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number).raise_alarm()
Chip Bolingab8863d2018-03-22 14:50:31 -0500857 returnValue('new-onu')
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500858
Chip Bolingfd1fd372017-12-20 13:34:12 -0600859 if serial_number_64 not in status.onus or onu_info['onu-id'] in self._active_los_alarms:
Chip Bolingff3087f2017-10-12 09:26:29 -0500860 onu = None
Chip Bolingfd1fd372017-12-20 13:34:12 -0600861 onu_id = onu_info['onu-id']
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500862
Chip Bolingab8863d2018-03-22 14:50:31 -0500863 if serial_number_64 in self._onus and onu_id in self._onu_by_id:
864 # Handles fast entry into this task before FPGA can set/clear results
865 returnValue('sticky-onu')
866
867 elif (serial_number_64 in self._onus and onu_id not in self._onu_by_id) or \
868 (serial_number_64 not in self._onus and onu_id in self._onu_by_id):
Chip Bolingfd1fd372017-12-20 13:34:12 -0600869 # May be here due to unmanaged power-cycle on OLT or fiber bounced for a
Chip Bolingbffef5e2018-08-07 14:53:12 -0500870 # previously activated ONU. Drop it and add back on next discovery cycle
Chip Bolingfd1fd372017-12-20 13:34:12 -0600871 self.delete_onu(onu_id)
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500872
873 elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
Chip Bolingff3087f2017-10-12 09:26:29 -0500874 self.log.warning('max-onus-provisioned', count=len(self._onus))
Chip Bolingab8863d2018-03-22 14:50:31 -0500875 returnValue('max-onus-reached')
Chip Bolingff3087f2017-10-12 09:26:29 -0500876
Chip Boling252c7772017-08-16 10:13:17 -0500877 else:
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500878 # TODO: Make use of upstream_channel_speed variable
879 onu = Onu(onu_info)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600880 self._onus[serial_number_64] = onu
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500881 self._onu_by_id[onu.onu_id] = onu
Chip Boling3e3b1a92017-05-16 11:51:18 -0500882
Chip Bolingff3087f2017-10-12 09:26:29 -0500883 if onu is not None:
884 try:
885 tconts = onu_info['t-conts']
886 gem_ports = onu_info['gem-ports']
Chip Boling252c7772017-08-16 10:13:17 -0500887
Chip Bolingff3087f2017-10-12 09:26:29 -0500888 # Add Multicast to PON on a per-ONU basis until xPON multicast support is ready
889 # In xPON/BBF, mcast gems tie back to the channel-pair
890 # MCAST VLAN IDs stored as a negative value
Chip Boling252c7772017-08-16 10:13:17 -0500891
Chip Bolingff3087f2017-10-12 09:26:29 -0500892 for id_or_vid, gem_port in gem_ports.iteritems(): # TODO: Deprecate this when BBF ready
893 try:
894 if gem_port.multicast:
895 self.log.debug('id-or-vid', id_or_vid=id_or_vid)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600896 vid = self.olt.multicast_vlans[0] if len(self.olt.multicast_vlans) else None
897 if vid is not None:
898 self.add_mcast_gem_port(gem_port, vid)
Chip Bolingff3087f2017-10-12 09:26:29 -0500899 except Exception as e:
900 self.log.exception('id-or-vid', e=e)
Chip Boling252c7772017-08-16 10:13:17 -0500901
Chip Bolingfd1fd372017-12-20 13:34:12 -0600902 yield onu.create(tconts, gem_ports)
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500903
Chip Bolingff3087f2017-10-12 09:26:29 -0500904 except Exception as e:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600905 self.log.exception('add-onu', serial_number=serial_number_64, e=e)
906 del self._onus[serial_number_64]
907 del self._onu_by_id[onu.onu_id]
Chip Boling3e3b1a92017-05-16 11:51:18 -0500908
Chip Boling3e3b1a92017-05-16 11:51:18 -0500909 def get_next_onu_id(self):
Chip Bolingfd1fd372017-12-20 13:34:12 -0600910 used_ids = [onu.onu_id for onu in self.onus]
Chip Boling3e3b1a92017-05-16 11:51:18 -0500911
912 while True:
Chip Boling5561d552017-07-07 15:11:26 -0500913 onu_id = self._next_onu_id
914 self._next_onu_id += 1
Chip Boling3e3b1a92017-05-16 11:51:18 -0500915
Chip Boling5561d552017-07-07 15:11:26 -0500916 if self._next_onu_id > Onu.MAX_ONU_ID:
Chip Boling27275992017-09-22 15:17:04 -0500917 self._next_onu_id = Onu.MIN_ONU_ID + 128
Chip Boling3e3b1a92017-05-16 11:51:18 -0500918
919 if onu_id not in used_ids:
920 return onu_id
921
Chip Bolingae298012017-08-28 08:55:17 -0500922 @inlineCallbacks
Chip Bolingff3087f2017-10-12 09:26:29 -0500923 def _remove_from_hardware(self, onu_id):
Chip Boling252c7772017-08-16 10:13:17 -0500924 uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, onu_id)
Chip Boling7294b252017-06-15 16:16:55 -0500925 name = 'pon-delete-onu-{}-{}'.format(self._pon_id, onu_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500926
Chip Bolingff3087f2017-10-12 09:26:29 -0500927 try:
928 yield self._parent.rest_client.request('DELETE', uri, name=name)
929
Chip Bolingfd1fd372017-12-20 13:34:12 -0600930 except RestInvalidResponseCode as e:
931 if e.code != 404:
932 self.log.exception('onu-delete', e=e)
933
Chip Bolingff3087f2017-10-12 09:26:29 -0500934 except Exception as e:
935 self.log.exception('onu-hw-delete', onu_id=onu_id, e=e)
936
937 @inlineCallbacks
938 def delete_onu(self, onu_id):
Chip Bolingae298012017-08-28 08:55:17 -0500939 onu = self._onu_by_id.get(onu_id)
940
Chip Boling252c7772017-08-16 10:13:17 -0500941 # Remove from any local dictionary
942 if onu_id in self._onu_by_id:
943 del self._onu_by_id[onu_id]
Chip Bolingff3087f2017-10-12 09:26:29 -0500944
Chip Bolingfd1fd372017-12-20 13:34:12 -0600945 for sn_64 in [onu.serial_number_64 for onu in self.onus if onu.onu_id == onu_id]:
946 del self._onus[sn_64]
Chip Bolingae298012017-08-28 08:55:17 -0500947
Chip Boling27275992017-09-22 15:17:04 -0500948 if onu is not None:
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500949 proxy = onu.proxy_address
Chip Bolingfd1fd372017-12-20 13:34:12 -0600950 try:
951 onu.delete()
Chip Boling252c7772017-08-16 10:13:17 -0500952
Chip Bolingfd1fd372017-12-20 13:34:12 -0600953 except Exception as e:
954 self.log.exception('onu-delete', serial_number=onu.serial_number, e=e)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500955
Chip Bolingfd1fd372017-12-20 13:34:12 -0600956 else:
957 try:
958 yield self._remove_from_hardware(onu_id)
959
960 except Exception as e:
961 self.log.exception('onu-remove', serial_number=onu.serial_number, e=e)
Chip Bolingae298012017-08-28 08:55:17 -0500962
Chip Boling27275992017-09-22 15:17:04 -0500963 def add_mcast_gem_port(self, mcast_gem, vlan):
Chip Bolingae298012017-08-28 08:55:17 -0500964 """
Chip Boling27275992017-09-22 15:17:04 -0500965 Add any new Multicast GEM Ports to the PON
966 :param mcast_gem: (GemPort)
Chip Bolingae298012017-08-28 08:55:17 -0500967 """
Chip Boling27275992017-09-22 15:17:04 -0500968 if vlan in self._mcast_gem_ports:
969 return
970
971 assert len(self._mcast_gem_ports) == 0, 'Only 1 MCAST GEMPort until BBF Support'
972 assert 1 <= vlan <= 4095, 'Invalid Multicast VLAN ID'
973 assert len(self.olt.multicast_vlans) == 1, 'Only support 1 MCAST VLAN until BBF Support'
974
975 self._mcast_gem_ports[vlan] = mcast_gem
Chip Boling252c7772017-08-16 10:13:17 -0500976
977 @inlineCallbacks
978 def channel_partition(self, name, partition=0, xpon_system=0, operation=None):
979 """
980 Delete/enable/disable a specified channel partition on this PON.
981
982 When creating a new Channel Partition, create it disabled, then define any associated
983 Channel Pairs. Then enable the Channel Partition.
984
985 :param name: (string) Name of the channel partition
986 :param partition: (int: 0..15) An index of the operator-specified channel subset
987 in a NG-PON2 system. For XGS-PON, this is typically 0
988 :param xpon_system: (int: 0..1048575) Identifies a specific xPON system
989 :param operation: (string) 'delete', 'enable', or 'disable'
990 """
991 if operation.lower() not in ['delete', 'enable', 'disable']:
992 raise ValueError('Unsupported operation: {}'.format(operation))
993
994 try:
995 xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
996
997 if operation.lower() is 'delete':
998 xml += '<interface operation="delete">'
999 else:
1000 xml += '<interface>'
1001 xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
1002 'adtn-xp:xpon-channel-partition</type>'
1003 xml += '<adtn-xp:channel-partition xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
1004 xml += ' <adtn-xp:partition-id>{}</adtn-xp:partition-id>'.format(partition)
1005 xml += ' <adtn-xp:xpon-system>{}</adtn-xp:xpon-system>'.format(xpon_system)
1006 xml += '</adtn-xp:channel-partition>'
1007 xml += '<enabled>{}</enabled>'.format('true' if operation.lower() == 'enable' else 'false')
1008
1009 xml += '<name>{}</name>'.format(name)
1010 xml += '</interface></interfaces>'
1011
1012 results = yield self.olt.netconf_client.edit_config(xml)
1013 returnValue(results)
1014
1015 except Exception as e:
1016 self.log.exception('channel_partition')
1017 raise
1018
1019 @inlineCallbacks
1020 def channel_pair(self, name, partition, operation=None, **kwargs):
1021 """
1022 Create/delete a channel pair on a specific channel_partition for a PON
1023
1024 :param name: (string) Name of the channel pair
1025 :param partition: (string) Name of the channel partition
1026 :param operation: (string) 'delete', 'enable', or 'disable'
1027 :param kwargs: (dict) Additional leaf settings if desired
1028 """
1029 if operation.lower() not in ['delete', 'enable', 'disable']:
1030 raise ValueError('Unsupported operation: {}'.format(operation))
1031
1032 try:
1033 xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
1034
1035 if operation.lower() is 'delete':
1036 xml += '<interface operation="delete">'
1037 else:
1038 xml += '<interface>'
1039 xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
1040 'adtn-xp:xpon-channel-pair</type>'
1041 xml += '<adtn-xp:channel-pair xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
1042 xml += ' <adtn-xp:channel-partition>{}</adtn-xp:channel-partition>'.format(partition)
1043 xml += ' <adtn-xp:channel-termination>channel-termination {}</adtn-xp:channel-termination>'.\
1044 format(self.pon_id)
1045 xml += ' <adtn-xp:upstream-admin-label>{}</adtn-xp:upstream-admin-label>'.\
1046 format(kwargs.get('upstream-admin-label', 1))
1047 xml += ' <adtn-xp:downstream-admin-label>{}</adtn-xp:downstream-admin-label>'.\
1048 format(kwargs.get('downstream-admin-label', 1))
1049 xml += ' <adtn-xp:upstream-channel-id>{}</adtn-xp:upstream-channel-id>'.\
1050 format(kwargs.get('upstream-channel-id', 15))
1051 xml += ' <adtn-xp:downstream-channel-id>{}</adtn-xp:downstream-channel-id>'.\
1052 format(kwargs.get('downstream-channel-id', 15))
1053 xml += ' <adtn-xp:downstream-channel-fec-enable>{}</adtn-xp:downstream-channel-fec-enable>'. \
1054 format('true' if kwargs.get('downstream-channel-fec-enable', True) else 'false')
1055 xml += ' <adtn-xp:upstream-channel-fec-enable>{}</adtn-xp:upstream-channel-fec-enable>'. \
1056 format('true' if kwargs.get('upstream-channel-fec-enable', True) else 'false')
1057 xml += '</adtn-xp:channel-pair>'
1058 # TODO: Add support for upstream/downstream FEC-enable coming from here and not hard-coded
1059
1060 xml += '<name>{}</name>'.format(name)
1061 xml += '</interface></interfaces>'
1062
1063 results = yield self.olt.netconf_client.edit_config(xml)
1064 returnValue(results)
1065
1066 except Exception as e:
1067 self.log.exception('channel_pair')
1068 raise