blob: 0505057adb01712a1713d8574548077d2fc94a64 [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
Chip Boling27275992017-09-22 15:17:04 -050017import arrow
Chip Boling3e3b1a92017-05-16 11:51:18 -050018
Chip Boling3e3b1a92017-05-16 11:51:18 -050019import structlog
Chip Boling7294b252017-06-15 16:16:55 -050020from enum import Enum
Chip Boling48646962017-08-20 09:41:18 -050021from twisted.internet import reactor, defer
Chip Boling7294b252017-06-15 16:16:55 -050022from twisted.internet.defer import inlineCallbacks, returnValue, succeed
Chip Boling3e3b1a92017-05-16 11:51:18 -050023
24from adtran_olt_handler import AdtranOltHandler
25from codec.olt_config import OltConfig
26from onu import Onu
27from voltha.protos.common_pb2 import OperStatus, AdminState
28from voltha.protos.device_pb2 import Device
29from voltha.protos.device_pb2 import Port
Chip Boling27275992017-09-22 15:17:04 -050030from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventState, AlarmEventCategory
Chip Boling3e3b1a92017-05-16 11:51:18 -050031
Chip Boling3e3b1a92017-05-16 11:51:18 -050032
33class PonPort(object):
34 """
35 A class similar to the 'Port' class in the VOLTHA
Chip Boling252c7772017-08-16 10:13:17 -050036
Chip Boling3e3b1a92017-05-16 11:51:18 -050037 TODO: Merge this with the Port class or cleanup where possible
38 so we do not duplicate fields/properties/methods
39 """
40 MAX_ONUS_SUPPORTED = 256
41 DEFAULT_ENABLED = False
Chip Boling27275992017-09-22 15:17:04 -050042 MAX_DEPLOYMENT_RANGE = 40000 # Meters
43
44 _MCAST_ONU_ID = 253
45 _MCAST_ALLOC_BASE = 0x500
Chip Boling3e3b1a92017-05-16 11:51:18 -050046
Chip Boling7294b252017-06-15 16:16:55 -050047 class State(Enum):
Chip Boling69fba862017-08-18 15:11:32 -050048 INITIAL = 0 # Created and initialization in progress
49 RUNNING = 1 # PON port contacted, ONU discovery active
50 STOPPED = 2 # Disabled
Chip Boling7294b252017-06-15 16:16:55 -050051 DELETING = 3 # Cleanup
52
Chip Boling252c7772017-08-16 10:13:17 -050053 _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery', 'autoactivate']
54 _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
55
Chip Boling61a12792017-10-02 13:23:27 -050056 def __init__(self, pon_index, port_no, parent):
Chip Boling7294b252017-06-15 16:16:55 -050057 # TODO: Weed out those properties supported by common 'Port' object (future)
Chip Boling252c7772017-08-16 10:13:17 -050058 self.log = structlog.get_logger(device_id=parent.device_id, pon_id=pon_index)
Chip Boling7294b252017-06-15 16:16:55 -050059
60 self._parent = parent
61 self._pon_id = pon_index
Chip Boling3e3b1a92017-05-16 11:51:18 -050062 self._port_no = port_no
Chip Boling252c7772017-08-16 10:13:17 -050063 self._name = 'xpon 0/{}'.format(pon_index+1)
Chip Boling61a12792017-10-02 13:23:27 -050064 self._label = 'pon-{}'.format(pon_index)
Chip Boling7294b252017-06-15 16:16:55 -050065 self._port = None
Chip Boling61a12792017-10-02 13:23:27 -050066 self._no_onu_discover_tick = 5.0
Chip Boling7294b252017-06-15 16:16:55 -050067 self._discovery_tick = 20.0
68 self._discovered_onus = [] # List of serial numbers
Chip Boling27275992017-09-22 15:17:04 -050069 self._sync_tick = 20.0
70 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -050071 self._expedite_sync = False
72 self._expedite_count = 0
73
Chip Boling252c7772017-08-16 10:13:17 -050074 self._onus = {} # serial_number-base64 -> ONU (allowed list)
75 self._onu_by_id = {} # onu-id -> ONU
Chip Boling27275992017-09-22 15:17:04 -050076 self._next_onu_id = Onu.MIN_ONU_ID + 128
77 self._mcast_gem_ports = {} # VLAN -> GemPort
Chip Boling7294b252017-06-15 16:16:55 -050078
Chip Boling252c7772017-08-16 10:13:17 -050079 self._admin_state = AdminState.DISABLED
80 self._oper_status = OperStatus.DISCOVERED
Chip Boling27275992017-09-22 15:17:04 -050081 self._state = PonPort.State.INITIAL
Chip Boling48646962017-08-20 09:41:18 -050082 self._deferred = None # General purpose
Chip Boling252c7772017-08-16 10:13:17 -050083 self._discovery_deferred = None # Specifically for ONU discovery
Chip Boling27275992017-09-22 15:17:04 -050084 self._sync_deferred = None # For sync of PON config to hardware
Chip Boling7294b252017-06-15 16:16:55 -050085
Chip Boling27275992017-09-22 15:17:04 -050086 self._active_los_alarms = set() # ONU-ID
87
88 # xPON configuration
Chip Boling7294b252017-06-15 16:16:55 -050089
Chip Boling252c7772017-08-16 10:13:17 -050090 self._xpon_name = None
Chip Boling27275992017-09-22 15:17:04 -050091 self._enabled = False
92 self._downstream_fec_enable = False
93 self._upstream_fec_enable = False
94 self._deployment_range = 25000
Chip Boling252c7772017-08-16 10:13:17 -050095 self._authentication_method = 'serial-number'
Chip Boling27275992017-09-22 15:17:04 -050096
97 if self.olt.autoactivate:
98 # Enable PON on startup
99 self._activation_method = 'autoactivate'
100 self._admin_state = AdminState.ENABLED
101 else:
102 self._activation_method = 'autodiscovery'
Chip Boling3e3b1a92017-05-16 11:51:18 -0500103
104 def __del__(self):
Chip Boling7294b252017-06-15 16:16:55 -0500105 self.stop()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500106
107 def __str__(self):
Chip Boling252c7772017-08-16 10:13:17 -0500108 return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
109 self._admin_state,
110 self._oper_status,
111 self.olt)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500112
113 def get_port(self):
114 """
115 Get the VOLTHA PORT object for this port
116 :return: VOLTHA Port object
117 """
Chip Boling7294b252017-06-15 16:16:55 -0500118 if self._port is None:
119 self._port = Port(port_no=self._port_no,
120 label=self._label,
121 type=Port.PON_OLT,
Chip Boling252c7772017-08-16 10:13:17 -0500122 admin_state=AdminState.ENABLED,
123 oper_status=OperStatus.ACTIVE)
124 # TODO: For now, no way to report the proper ADMIN or OPER status
125 # admin_state=self._admin_state,
126 # oper_status=self._oper_status)
Chip Boling7294b252017-06-15 16:16:55 -0500127 return self._port
Chip Boling3e3b1a92017-05-16 11:51:18 -0500128
129 @property
130 def port_number(self):
131 return self._port_no
132
133 @property
Chip Boling5561d552017-07-07 15:11:26 -0500134 def name(self):
135 return self._name
136
137 @property
Chip Boling252c7772017-08-16 10:13:17 -0500138 def xpon_name(self):
139 return self._xpon_name
140
141 @xpon_name.setter
142 def xpon_name(self, value):
Chip Boling61a12792017-10-02 13:23:27 -0500143 assert '/' not in value, "xPON names cannot have embedded forward slashes '/'"
Chip Boling252c7772017-08-16 10:13:17 -0500144 self._xpon_name = value
145
146 @property
Chip Boling3e3b1a92017-05-16 11:51:18 -0500147 def pon_id(self):
Chip Boling7294b252017-06-15 16:16:55 -0500148 return self._pon_id
149
150 @property
151 def olt(self):
152 return self._parent
153
154 @property
155 def state(self):
156 return self._state
157
158 @property
Chip Bolingae298012017-08-28 08:55:17 -0500159 def admin_state(self):
160 return self._admin_state
161
162 @admin_state.setter
163 def admin_state(self, value):
164 if self._admin_state != value:
165 self._admin_state = value
166 if self._admin_state == AdminState.ENABLED:
167 self.start()
168 else:
169 self.stop()
170
171 @property
Chip Boling7294b252017-06-15 16:16:55 -0500172 def adapter_agent(self):
173 return self.olt.adapter_agent
Chip Boling3e3b1a92017-05-16 11:51:18 -0500174
Chip Boling252c7772017-08-16 10:13:17 -0500175 @property
Chip Boling27275992017-09-22 15:17:04 -0500176 def enabled(self):
177 return self._enabled
178
179 @enabled.setter
180 def enabled(self, value):
181 assert isinstance(value, bool), 'enabled is a boolean'
182 if self._enabled != value:
183 if value:
184 self.start()
185 self.stop()
186
187 @property
188 def downstream_fec_enable(self):
189 return self._downstream_fec_enable
190
191 @downstream_fec_enable.setter
192 def downstream_fec_enable(self, value):
193 assert isinstance(value, bool), 'downstream FEC enabled is a boolean'
194
195 if self._downstream_fec_enable != value:
196 self._downstream_fec_enable = value
197 if self._state == PonPort.State.RUNNING:
198 self._deferred = self._set_pon_config("downstream-fec-enable", value)
199
200 @property
201 def upstream_fec_enable(self):
202 return self._upstream_fec_enable
203
204 @upstream_fec_enable.setter
205 def upstream_fec_enable(self, value):
206 assert isinstance(value, bool), 'upstream FEC enabled is a boolean'
207
208 if self._upstream_fec_enable != value:
209 self._upstream_fec_enable = value
210 if self._state == PonPort.State.RUNNING:
211 self._deferred = self._set_pon_config("upstream-fec-enable", value)
212
213 @property
214 def deployment_range(self):
215 """Maximum deployment range (in meters)"""
216 return self._deployment_range
217
218 @deployment_range.setter
219 def deployment_range(self, value):
220 """Maximum deployment range (in meters)"""
221 if not 0 <= value <= PonPort.MAX_DEPLOYMENT_RANGE:
222 raise ValueError('Deployment range should be 0..{} meters'.
223 format(PonPort.MAX_DEPLOYMENT_RANGE))
224 if self._deployment_range != value:
225 self._deployment_range = value
226 if self._state == PonPort.State.RUNNING:
227 self._deferred = self._set_pon_config("deployment-range", value)
228
229 @property
Chip Boling252c7772017-08-16 10:13:17 -0500230 def discovery_tick(self):
231 return self._discovery_tick * 10
232
233 @discovery_tick.setter
234 def discovery_tick(self, value):
235 if value < 0:
236 raise ValueError("Polling interval must be >= 0")
237
238 if self.discovery_tick != value:
239 self._discovery_tick = value / 10
240
Chip Boling27275992017-09-22 15:17:04 -0500241 try:
242 if self._discovery_deferred is not None and \
243 not self._discovery_deferred.called:
244 self._discovery_deferred.cancel()
245 except:
246 pass
247 self._discovery_deferred = None
Chip Boling252c7772017-08-16 10:13:17 -0500248
249 if self._discovery_tick > 0:
250 self._discovery_deferred = reactor.callLater(self._discovery_tick,
251 self._discover_onus)
252
253 @property
254 def activation_method(self):
255 return self._activation_method
256
257 @activation_method.setter
258 def activation_method(self, value):
259 value = value.lower()
260 if value not in PonPort._SUPPORTED_ACTIVATION_METHODS:
261 raise ValueError('Invalid ONU activation method')
262 self._activation_method = value
263
264 @property
265 def authentication_method(self):
266 return self._authentication_method
267
268 @authentication_method.setter
269 def authentication_method(self, value):
270 value = value.lower()
271 if value not in PonPort._SUPPORTED_AUTHENTICATION_METHODS:
272 raise ValueError('Invalid ONU authentication method')
273 self._authentication_method = value
274
Chip Boling3e3b1a92017-05-16 11:51:18 -0500275 def get_logical_port(self):
276 """
Chip Boling7294b252017-06-15 16:16:55 -0500277 Get the VOLTHA logical port for this port. For PON ports, a logical port
278 is not currently created, so always return None
279
Chip Boling3e3b1a92017-05-16 11:51:18 -0500280 :return: VOLTHA logical port or None if not supported
281 """
282 return None
283
Chip Boling7294b252017-06-15 16:16:55 -0500284 def _cancel_deferred(self):
Chip Boling252c7772017-08-16 10:13:17 -0500285 d1, self._deferred = self._deferred, None
286 d2, self._discovery_deferred = self._discovery_deferred, None
Chip Boling27275992017-09-22 15:17:04 -0500287 d3, self._sync_deferred = self._sync_deferred, None
Chip Boling48646962017-08-20 09:41:18 -0500288
Chip Boling27275992017-09-22 15:17:04 -0500289 for d in [d1, d2, d3]:
Chip Boling48646962017-08-20 09:41:18 -0500290 try:
Chip Boling27275992017-09-22 15:17:04 -0500291 if d is not None and not d.called:
292 d.cancel()
Chip Boling48646962017-08-20 09:41:18 -0500293 except Exception as e:
294 pass
Chip Boling7294b252017-06-15 16:16:55 -0500295
296 def _update_adapter_agent(self):
297 # TODO: Currently the adapter_agent does not allow 'update' of port status
298 # self.adapter_agent.update_port(self.olt.device_id, self.get_port())
299 pass
300
Chip Boling3e3b1a92017-05-16 11:51:18 -0500301 def start(self):
302 """
303 Start/enable this PON and start ONU discover
304 :return: (deferred)
305 """
Chip Boling7294b252017-06-15 16:16:55 -0500306 if self._state == PonPort.State.RUNNING:
307 return succeed('Running')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500308
Chip Boling252c7772017-08-16 10:13:17 -0500309 self.log.info('start')
Chip Boling7294b252017-06-15 16:16:55 -0500310
311 self._cancel_deferred()
312 self._state = PonPort.State.INITIAL
Chip Boling252c7772017-08-16 10:13:17 -0500313 self._oper_status = OperStatus.ACTIVATING
Chip Boling27275992017-09-22 15:17:04 -0500314 self._enabled = True
Chip Boling7294b252017-06-15 16:16:55 -0500315
316 # Do the rest of the startup in an async method
Chip Boling5561d552017-07-07 15:11:26 -0500317 self._deferred = reactor.callLater(0.5, self._finish_startup)
Chip Boling252c7772017-08-16 10:13:17 -0500318 self._update_adapter_agent()
319
Chip Boling27275992017-09-22 15:17:04 -0500320 return succeed('Scheduled')
Chip Boling7294b252017-06-15 16:16:55 -0500321
322 @inlineCallbacks
323 def _finish_startup(self):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500324 """
Chip Boling7294b252017-06-15 16:16:55 -0500325 Do all startup offline since REST may fail
Chip Boling3e3b1a92017-05-16 11:51:18 -0500326 """
Chip Boling7294b252017-06-15 16:16:55 -0500327 if self._state != PonPort.State.INITIAL:
328 returnValue('Done')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500329
Chip Boling252c7772017-08-16 10:13:17 -0500330 self.log.debug('final-startup')
Chip Boling5561d552017-07-07 15:11:26 -0500331
Chip Boling27275992017-09-22 15:17:04 -0500332 try:
333 self._deferred = self._get_pon_config()
334 results = yield self._deferred
335
336 except Exception as e:
337 self.log.exception('initial-GET', e=e)
338 self._deferred = reactor.callLater(5, self._finish_startup)
339 returnValue(self._deferred)
340
341 # Load config from hardware
342
343 enabled = results.get('enabled', False)
344 downstream_fec_enable = results.get('downstream-fec-enable', False)
345 upstream_fec_enable = results.get('upstream-fec-enable', False)
346 deployment_range = results.get('deployment-range', 25000)
347 self._in_sync = True
348
349 if enabled != self._enabled:
Chip Boling7294b252017-06-15 16:16:55 -0500350 try:
Chip Boling27275992017-09-22 15:17:04 -0500351 self._deferred = self._set_pon_config("enabled", True)
352 yield self._deferred
Chip Boling3e3b1a92017-05-16 11:51:18 -0500353
Chip Boling7294b252017-06-15 16:16:55 -0500354 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -0500355 self.log.exception('final-startup-enable', e=e)
Chip Boling7294b252017-06-15 16:16:55 -0500356 self._deferred = reactor.callLater(3, self._finish_startup)
357 returnValue(self._deferred)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500358
Chip Boling27275992017-09-22 15:17:04 -0500359 if downstream_fec_enable != self._downstream_fec_enable:
Chip Boling7294b252017-06-15 16:16:55 -0500360 try:
Chip Boling27275992017-09-22 15:17:04 -0500361 self._deferred = self._set_pon_config("downstream-fec-enable",
362 self._downstream_fec_enable)
363 yield self._deferred
Chip Boling3e3b1a92017-05-16 11:51:18 -0500364
Chip Boling7294b252017-06-15 16:16:55 -0500365 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500366 self.log.warning('final-startup-downstream-FEC', e=e)
367 self._in_sync = False
368 # Non-fatal. May have failed due to no SFQ in slot
Chip Boling7294b252017-06-15 16:16:55 -0500369
Chip Boling27275992017-09-22 15:17:04 -0500370 if upstream_fec_enable != self._upstream_fec_enable:
Chip Boling7294b252017-06-15 16:16:55 -0500371 try:
Chip Boling27275992017-09-22 15:17:04 -0500372 self._deferred = self._set_pon_config("upstream-fec-enable",
373 self._upstream_fec_enable)
374 yield self._deferred
Chip Boling7294b252017-06-15 16:16:55 -0500375
376 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500377 self.log.warning('final-startup-upstream-FEC', e=e)
378 self._in_sync = False
379 # Non-fatal. May have failed due to no SFQ in slot
Chip Boling7294b252017-06-15 16:16:55 -0500380
Chip Boling27275992017-09-22 15:17:04 -0500381 if deployment_range != self._deployment_range:
382 try:
383 self._deferred = self._set_pon_config("deployment-range",
384 self._deployment_range)
385 yield self._deferred
Chip Boling7294b252017-06-15 16:16:55 -0500386
Chip Boling27275992017-09-22 15:17:04 -0500387 except Exception as e:
388 self.log.warning('final-startup-deployment-range', e=e)
389 self._in_sync = False
390 # Non-fatal. May have failed due to no SFQ in slot
Chip Boling7294b252017-06-15 16:16:55 -0500391
Chip Boling27275992017-09-22 15:17:04 -0500392 # If here, initial settings were successfully written to hardware
Chip Boling48646962017-08-20 09:41:18 -0500393
Chip Boling27275992017-09-22 15:17:04 -0500394 self._admin_state = AdminState.ENABLED
395 self._oper_status = OperStatus.ACTIVE # TODO: is this correct, how do we tell GRPC
396 self._state = PonPort.State.RUNNING
Chip Boling48646962017-08-20 09:41:18 -0500397
Chip Boling27275992017-09-22 15:17:04 -0500398 # Restart any ONU's in case here due to reboot
Chip Boling252c7772017-08-16 10:13:17 -0500399
Chip Boling27275992017-09-22 15:17:04 -0500400 if len(self._onus) > 0:
401 dl = []
402 for onu in self._onus.itervalues():
403 dl.append(onu.restart())
404 yield defer.gatherResults(dl, consumeErrors=True)
Chip Boling7294b252017-06-15 16:16:55 -0500405
Chip Boling27275992017-09-22 15:17:04 -0500406 # Begin to ONU discovery and hardware sync
Chip Boling3e3b1a92017-05-16 11:51:18 -0500407
Chip Boling27275992017-09-22 15:17:04 -0500408 self._discovery_deferred = reactor.callLater(5, self._discover_onus)
409 self._sync_deferred = reactor.callLater(60, self._sync_hardware)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500410
Chip Boling27275992017-09-22 15:17:04 -0500411 self._update_adapter_agent()
412 returnValue('Enabled')
Chip Boling7294b252017-06-15 16:16:55 -0500413
Chip Boling27275992017-09-22 15:17:04 -0500414 @inlineCallbacks
Chip Boling3e3b1a92017-05-16 11:51:18 -0500415 def stop(self):
Chip Boling7294b252017-06-15 16:16:55 -0500416 if self._state == PonPort.State.STOPPED:
Chip Boling27275992017-09-22 15:17:04 -0500417 self.log.debug('already stopped')
418 returnValue(succeed('Stopped'))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500419
Chip Boling252c7772017-08-16 10:13:17 -0500420 self.log.info('stopping')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500421
Chip Boling7294b252017-06-15 16:16:55 -0500422 self._cancel_deferred()
Chip Boling27275992017-09-22 15:17:04 -0500423 self._enabled = False
424 results = yield self._set_pon_config("enabled", False)
425 self._sync_deferred = reactor.callLater(self._sync_tick, self._sync_hardware)
Chip Boling7294b252017-06-15 16:16:55 -0500426
427 self._admin_state = AdminState.DISABLED
428 self._oper_status = OperStatus.UNKNOWN
429 self._update_adapter_agent()
430
431 self._state = PonPort.State.STOPPED
Chip Boling27275992017-09-22 15:17:04 -0500432 self.log.debug('stopped')
433 returnValue(results)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500434
435 @inlineCallbacks
436 def reset(self):
Chip Boling7294b252017-06-15 16:16:55 -0500437 """
438 Set the PON Port to a known good state on initial port startup. Actual
439 PON 'Start' is done elsewhere
440 """
441 if self._state != PonPort.State.INITIAL:
Chip Boling252c7772017-08-16 10:13:17 -0500442 self.log.error('reset-ignored', state=self._state)
Chip Boling7294b252017-06-15 16:16:55 -0500443 returnValue('Ignored')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500444
Chip Boling27275992017-09-22 15:17:04 -0500445 initial_port_state = AdminState.ENABLED if self.olt.autoactivate else AdminState.DISABLED
446 self.log.info('reset', initial_state=initial_port_state)
Chip Boling7294b252017-06-15 16:16:55 -0500447
Chip Boling252c7772017-08-16 10:13:17 -0500448 try:
Chip Boling27275992017-09-22 15:17:04 -0500449 self._deferred = self._get_pon_config()
Chip Boling252c7772017-08-16 10:13:17 -0500450 results = yield self._deferred
Chip Boling27275992017-09-22 15:17:04 -0500451 enabled = results.get('enabled', False)
Chip Boling252c7772017-08-16 10:13:17 -0500452
453 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500454 self.log.exception('get-config', e=e)
455 enabled = False
Chip Boling252c7772017-08-16 10:13:17 -0500456
Chip Boling27275992017-09-22 15:17:04 -0500457 enable = initial_port_state == AdminState.ENABLED
Chip Boling252c7772017-08-16 10:13:17 -0500458
Chip Boling27275992017-09-22 15:17:04 -0500459 if enable != enabled:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500460 try:
Chip Boling27275992017-09-22 15:17:04 -0500461 self._deferred = yield self._set_pon_config("enabled", enable)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500462 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500463 self.log.exception('reset-enabled', e=e, enabled=enabled)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500464
Chip Boling27275992017-09-22 15:17:04 -0500465 # TODO: Move to 'set_pon_config' method and also make sure GRPC/Port is ok
466 self._admin_state = AdminState.ENABLED if enable else AdminState.DISABLED
Chip Boling3e3b1a92017-05-16 11:51:18 -0500467
Chip Boling252c7772017-08-16 10:13:17 -0500468 try:
Chip Boling27275992017-09-22 15:17:04 -0500469 # Walk the provisioned ONU list and disable any exiting ONUs
470 results = yield self._get_onu_config()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500471
Chip Boling252c7772017-08-16 10:13:17 -0500472 if isinstance(results, list) and len(results) > 0:
473 onu_configs = OltConfig.Pon.Onu.decode(results)
Chip Boling27275992017-09-22 15:17:04 -0500474 dl = []
Chip Boling252c7772017-08-16 10:13:17 -0500475 for onu_id in onu_configs.iterkeys():
Chip Boling27275992017-09-22 15:17:04 -0500476 dl.append(self.delete_onu(onu_id))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500477
Chip Boling27275992017-09-22 15:17:04 -0500478 try:
479 if len(dl) > 0:
480 yield defer.gatherResults(dl, consumeErrors=True)
481
482 except Exception as e:
483 self.log.exception('rest-ONU-delete', onu_id=onu_id, e=e)
484 pass # Non-fatal
Chip Boling252c7772017-08-16 10:13:17 -0500485
486 except Exception as e:
487 self.log.exception('onu-delete', e=e)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500488
Chip Boling5561d552017-07-07 15:11:26 -0500489 returnValue('Reset complete')
490
Chip Boling69fba862017-08-18 15:11:32 -0500491 def restart(self):
492 if self._state == PonPort.State.RUNNING or self._state == PonPort.State.STOPPED:
493 start_it = (self._state == PonPort.State.RUNNING)
494 self._state = PonPort.State.INITIAL
Chip Boling48646962017-08-20 09:41:18 -0500495
Chip Boling69fba862017-08-18 15:11:32 -0500496 return self.start() if start_it else self.stop()
497 return succeed('nop')
498
Chip Boling7294b252017-06-15 16:16:55 -0500499 def delete(self):
500 """
501 Parent device is being deleted. Do not change any config but
502 stop all polling
503 """
Chip Boling252c7772017-08-16 10:13:17 -0500504 self.log.info('Deleting')
Chip Boling7294b252017-06-15 16:16:55 -0500505 self._state = PonPort.State.DELETING
506 self._cancel_deferred()
507
Chip Boling27275992017-09-22 15:17:04 -0500508 def gem_ids(self, vid, exception_gems, multicast_gems):
Chip Boling252c7772017-08-16 10:13:17 -0500509 """
510 Get all GEM Port IDs used on a given PON
511
Chip Boling27275992017-09-22 15:17:04 -0500512 :param vid: (int) VLAN ID if customer ONU specific. None if for all ONUs
513 on PON, if Multicast, VID for Multicast, or None for all\
514 Multicast GEMPorts
Chip Boling69fba862017-08-18 15:11:32 -0500515 :param exception_gems: (boolean) Select from special purpose ACL GEM-Portas
Chip Boling27275992017-09-22 15:17:04 -0500516 :param multicast_gems: (boolean) Select from available Multicast GEM Ports
517 :return: (dict) data_gem -> key -> onu-id, value -> tuple(sorted list of GEM Port IDs, onu_vid)
518 mcast_gem-> key -> mcast-vid, value -> GEM Port IDs
Chip Boling252c7772017-08-16 10:13:17 -0500519 """
520 gem_ids = {}
Chip Boling27275992017-09-22 15:17:04 -0500521
522 if multicast_gems:
523 # Multicast GEMs belong to the PON, but we may need to register them on
524 # all ONUs. Rework when BBF MCAST Gems are supported
525 for vlan, gem_port in self._mcast_gem_ports.iteritems():
526 if vid is None or (vid == vlan and vid in self.olt.multicast_vlans):
527 gem_ids[vlan] = ([gem_port.gem_id], None)
528 else:
529 for onu_id, onu in self._onu_by_id.iteritems():
530 if vid is None or vid == onu.onu_vid:
531 gem_ids[onu_id] = (onu.gem_ids(exception_gems), onu.onu_vid) # FIXED_ONU
532
Chip Boling252c7772017-08-16 10:13:17 -0500533 return gem_ids
534
Chip Boling27275992017-09-22 15:17:04 -0500535 def _get_pon_config(self):
Chip Boling7294b252017-06-15 16:16:55 -0500536 uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id)
537 name = 'pon-get-config-{}'.format(self._pon_id)
538 return self._parent.rest_client.request('GET', uri, name=name)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500539
Chip Boling27275992017-09-22 15:17:04 -0500540 def _get_onu_config(self, onu_id=None):
Chip Boling252c7772017-08-16 10:13:17 -0500541 if onu_id is None:
542 uri = AdtranOltHandler.GPON_ONU_CONFIG_LIST_URI.format(self._pon_id)
543 else:
544 uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, onu_id)
545
Chip Boling7294b252017-06-15 16:16:55 -0500546 name = 'pon-get-onu_config-{}-{}'.format(self._pon_id, onu_id)
547 return self._parent.rest_client.request('GET', uri, name=name)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500548
Chip Boling27275992017-09-22 15:17:04 -0500549 def _set_pon_config(self, leaf, value):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500550 data = json.dumps({leaf: value})
Chip Boling7294b252017-06-15 16:16:55 -0500551 uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id)
552 name = 'pon-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value))
553 return self._parent.rest_client.request('PATCH', uri, data=data, name=name)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500554
Chip Boling252c7772017-08-16 10:13:17 -0500555 def _discover_onus(self):
Chip Boling27275992017-09-22 15:17:04 -0500556 self.log.debug('discovery', state=self._admin_state, in_sync=self._in_sync)
Chip Boling7294b252017-06-15 16:16:55 -0500557 if self._admin_state == AdminState.ENABLED:
Chip Boling27275992017-09-22 15:17:04 -0500558 if self._in_sync:
559 data = json.dumps({'pon-id': self._pon_id})
560 uri = AdtranOltHandler.GPON_PON_DISCOVER_ONU
561 name = 'pon-discover-onu-{}'.format(self._pon_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500562
Chip Boling27275992017-09-22 15:17:04 -0500563 self._discovery_deferred = self._parent.rest_client.request('POST', uri, data, name=name)
564 self._discovery_deferred.addBoth(self._onu_discovery_init_complete)
565 else:
566 self.discovery_deferred = reactor.callLater(0,
567 self._onu_discovery_init_complete,
568 None)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500569
Chip Boling252c7772017-08-16 10:13:17 -0500570 def _onu_discovery_init_complete(self, _):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500571 """
572 This method is called after the REST POST to request ONU discovery is
573 completed. The results (body) of the post is always empty / 204 NO CONTENT
574 """
Chip Boling7294b252017-06-15 16:16:55 -0500575 delay = self._no_onu_discover_tick if len(self._onus) == 0 else self._discovery_tick
Chip Boling3e3b1a92017-05-16 11:51:18 -0500576 delay += random.uniform(-delay / 10, delay / 10)
Chip Boling252c7772017-08-16 10:13:17 -0500577 self._discovery_deferred = reactor.callLater(delay, self._discover_onus)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500578
Chip Boling27275992017-09-22 15:17:04 -0500579 def _sync_hardware(self):
580 if self._state == PonPort.State.RUNNING or self._state == PonPort.State.STOPPED:
581 def read_config(results):
582 self.log.debug('read-config', results=results)
583 config = OltConfig.Pon.decode([results])
584 assert self.pon_id in config, 'sync-pon-not-found-{}'.format(self.pon_id)
585 config = config[self.pon_id]
586 self._in_sync = True
587
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500588 dl = []
Chip Boling27275992017-09-22 15:17:04 -0500589
590 if self.enabled != config.enabled:
591 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500592 self._expedite_sync = True
Chip Boling27275992017-09-22 15:17:04 -0500593 dl.append(self._set_pon_config("enabled", self.enabled))
594
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500595 elif self._state == PonPort.State.RUNNING:
596 if self.deployment_range != config.deployment_range:
597 self._in_sync = False
598 self._expedite_sync = True
599 dl.append(self._set_pon_config("deployment-range",
600 self.deployment_range))
601
Chip Boling27275992017-09-22 15:17:04 -0500602 if self.downstream_fec_enable != config.downstream_fec_enable:
603 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500604 self._expedite_sync = True
Chip Boling27275992017-09-22 15:17:04 -0500605 dl.append(self._set_pon_config("downstream-fec-enable",
606 self.downstream_fec_enable))
607
608 if self.upstream_fec_enable != config.upstream_fec_enable:
609 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500610 self._expedite_sync = True
Chip Boling27275992017-09-22 15:17:04 -0500611 dl.append(self._set_pon_config("upstream-fec-enable",
612 self.upstream_fec_enable))
Chip Boling27275992017-09-22 15:17:04 -0500613 return defer.gatherResults(dl)
614
615 def sync_onus(results):
616 if self._state == PonPort.State.RUNNING:
617 self.log.debug('sync-pon-results', results=results)
618 assert isinstance(results, list), 'expected-list'
619 assert isinstance(results[0], OltConfig.Pon), 'expected-pon-at-front'
620 hw_onus = results[0].onus
621
622 # ONU's have their own sync task, extra (should be deleted) are
623 # handled here. Missing are handled by normal discovery mechanisms.
624
625 hw_onu_ids = frozenset([onu.onu_id for onu in hw_onus])
626 my_onu_ids = frozenset(self._onu_by_id.keys())
627
628 extra_onus = hw_onu_ids - my_onu_ids
629 dl = [self.delete_onu(onu_id) for onu_id in extra_onus]
630
Chip Boling27275992017-09-22 15:17:04 -0500631 return defer.gatherResults(dl, consumeErrors=True)
632
633 def failure(reason, what):
634 self.log.error('hardware-sync-{}-failed'.format(what), reason=reason)
635 self._in_sync = False
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500636 self._expedite_sync = False
Chip Boling27275992017-09-22 15:17:04 -0500637
638 def reschedule(_):
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500639 # Speed up sequential resync a limited number of times if out of sync.
640
Chip Boling27275992017-09-22 15:17:04 -0500641 delay = self._sync_tick
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500642
643 if self._expedite_sync:
644 self._expedite_count += 1
645 if self._expedite_count < 5:
646 delay = 1
647 else:
648 self._expedite_count = 0
649
Chip Boling27275992017-09-22 15:17:04 -0500650 delay += random.uniform(-delay / 10, delay / 10)
651 self._sync_deferred = reactor.callLater(delay, self._sync_hardware)
652
653 self._sync_deferred = self._get_pon_config()
654 self._sync_deferred.addCallbacks(read_config, failure, errbackArgs=['get-config'])
655 self._sync_deferred.addCallbacks(sync_onus, failure, errbackArgs=['pon-sync'])
656 self._sync_deferred.addBoth(reschedule)
657
Chip Boling3e3b1a92017-05-16 11:51:18 -0500658 def process_status_poll(self, status):
659 """
660 Process PON status poll request
661
662 :param status: (OltState.Pon object) results from RESTCONF GET
663 """
Chip Boling252c7772017-08-16 10:13:17 -0500664 self.log.debug('process-status-poll', status=status)
Chip Boling7294b252017-06-15 16:16:55 -0500665
666 if self._admin_state != AdminState.ENABLED:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500667 return
668
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500669 # Get new/missing from the discovered ONU leaf. Stale ONUs from previous
670 # configs are now cleaned up during h/w re-sync/reflow.
Chip Boling3e3b1a92017-05-16 11:51:18 -0500671
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500672 new, rediscovered_onus = self._process_status_onu_discovered_list(status.discovered_onu)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500673
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500674 # Process newly discovered ONU list and rediscovered ONUs
Chip Boling3e3b1a92017-05-16 11:51:18 -0500675
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500676 for serial_number in new | rediscovered_onus:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500677 reactor.callLater(0, self.add_onu, serial_number, status)
678
Chip Boling27275992017-09-22 15:17:04 -0500679 # Process LOS list
680 self._process_los_alarms(frozenset(status.ont_los))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500681
Chip Boling27275992017-09-22 15:17:04 -0500682 # Process ONU info. Note that newly added ONUs will not be processed
683 # until the next pass
684
685 self._update_onu_status(status.onus)
686
687 def _update_onu_status(self, onus):
688 """
689 Process ONU status for this PON
690 :param onus: (dict) onu_id: ONU State
691 """
692 for onu_id, onu_status in onus.iteritems():
693 if onu_id in self._onu_by_id:
694 self._onu_by_id[onu_id].rssi = onu_status.rssi
695 self._onu_by_id[onu_id].equalization_delay = onu_status.equalization_delay
696 self._onu_by_id[onu_id].fiber_length = onu_status.fiber_length
697
698 def _process_los_alarms(self, ont_los):
699 """
700 Walk current LOS and set/clear LOS as appropriate
701 :param ont_los: (frozenset) ONU IDs of ONUs in LOS alarm state
702 """
703 cleared_alarms = self._active_los_alarms - ont_los
704 new_alarms = ont_los - self._active_los_alarms
705
706 def los_alarm(status, _id):
707 alarm = 'LOS'
708 alarm_data = {
709 'ts': arrow.utcnow().timestamp,
710 'description': self.olt.alarms.format_description('onu LOS', alarm, status),
711 'id': self.olt.alarms.format_id(alarm),
712 'type': AlarmEventType.COMMUNICATION,
713 'category': AlarmEventCategory.ONT,
714 'severity': AlarmEventSeverity.MAJOR,
715 'state': AlarmEventState.RAISED if status else AlarmEventState.CLEARED
716 }
717 context_data = {'onu_id': _id}
718 self.olt.alarms.send_alarm(context_data, alarm_data)
719
720 if len(cleared_alarms) > 0 or len(new_alarms) > 0:
721 self.log.info('onu-los', cleared=cleared_alarms, new=new_alarms)
722
723 for onu_id in cleared_alarms:
724 # TODO: test 'clear' of LOS alarm when you delete an ONU in LOS
725 self._active_los_alarms.remove(onu_id)
726 los_alarm(False, onu_id)
727
728 for onu_id in new_alarms:
729 self._active_los_alarms.add(onu_id)
730 los_alarm(True, onu_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500731
Chip Boling61a12792017-10-02 13:23:27 -0500732 # TODO: A method to update the AdapterAgent's child device state (operStatus)
733 # would be useful here
734
Chip Boling3e3b1a92017-05-16 11:51:18 -0500735 def _process_status_onu_discovered_list(self, discovered_onus):
736 """
Chip Bolingae298012017-08-28 08:55:17 -0500737 Look for new ONUs
Chip Boling3e3b1a92017-05-16 11:51:18 -0500738
739 :param discovered_onus: (frozenset) Set of ONUs currently discovered
740 """
Chip Boling252c7772017-08-16 10:13:17 -0500741 self.log.debug('discovered-ONUs', list=discovered_onus)
742
743 # Only request discovery if activation is auto-discovery or auto-activate
744 continue_discovery = ['autodiscovery', 'autoactivate']
745
746 if self._activation_method not in continue_discovery:
747 return set(), set()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500748
Chip Boling7294b252017-06-15 16:16:55 -0500749 my_onus = frozenset(self._onus.keys())
Chip Boling3e3b1a92017-05-16 11:51:18 -0500750
751 new_onus = discovered_onus - my_onus
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500752 rediscovered_onus = my_onus & discovered_onus
Chip Boling3e3b1a92017-05-16 11:51:18 -0500753
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500754 return new_onus, rediscovered_onus
Chip Boling3e3b1a92017-05-16 11:51:18 -0500755
Chip Boling252c7772017-08-16 10:13:17 -0500756 def _get_onu_info(self, serial_number):
757 """
758 Parse through available xPON information for ONU configuration settings
759 :param serial_number: (string) Decoded (not base64) serial number string
760 :return: (dict) onu config data or None on lookup failure
761 """
762 try:
Chip Boling27275992017-09-22 15:17:04 -0500763 from flow.demo_data import get_tconts, get_gem_ports, get_onu_id
Chip Boling252c7772017-08-16 10:13:17 -0500764
765 if self.activation_method == "autoactivate":
Chip Boling27275992017-09-22 15:17:04 -0500766 onu_id = get_onu_id(serial_number)
767 if onu_id is None:
768 onu_id = self.get_next_onu_id()
Chip Boling252c7772017-08-16 10:13:17 -0500769 enabled = True
770 channel_speed = 0
Chip Bolingae298012017-08-28 08:55:17 -0500771 tconts = get_tconts(serial_number, onu_id)
772 gem_ports = get_gem_ports(serial_number, onu_id)
Chip Boling252c7772017-08-16 10:13:17 -0500773
774 elif self.activation_method == "autodiscovery":
775 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 Bolingae298012017-08-28 08:55:17 -0500780 vont_info = next(info for _, info in gpon_info['v-ont-anis'].items()
Chip Boling252c7772017-08-16 10:13:17 -0500781 if info.get('expected-serial-number') == serial_number)
782
783 onu_id = vont_info['onu-id']
784 enabled = vont_info['enabled']
785 channel_speed = vont_info['upstream-channel-speed']
786
Chip Bolingae298012017-08-28 08:55:17 -0500787 tconts = {key: val for key, val in gpon_info['tconts'].iteritems()
788 if val.vont_ani == vont_info['name']}
789 tcont_names = set(tconts.keys())
790
791 gem_ports = {key: val for key, val in gpon_info['gem-ports'].iteritems()
792 if val.tconf_ref in tcont_names}
793
Chip Boling252c7772017-08-16 10:13:17 -0500794 except StopIteration:
Chip Bolingae298012017-08-28 08:55:17 -0500795 return None # Can happen if vont-ani has not yet been configured
Chip Boling252c7772017-08-16 10:13:17 -0500796 else:
797 return None
798 else:
799 return None
800
801 onu_info = {
Chip Boling27275992017-09-22 15:17:04 -0500802 'device-id': self.olt.device_id,
Chip Boling252c7772017-08-16 10:13:17 -0500803 'serial-number': serial_number,
804 'xpon-name': None,
805 'pon': self,
806 'onu-id': onu_id,
807 'enabled': enabled,
808 'upstream-channel-speed': channel_speed,
809 'password': Onu.DEFAULT_PASSWORD,
Chip Bolingae298012017-08-28 08:55:17 -0500810 't-conts': tconts,
811 'gem-ports': gem_ports,
Chip Boling27275992017-09-22 15:17:04 -0500812 'onu-vid': self.olt.get_channel_id(self._pon_id, onu_id),
813 'channel-id': self.olt.get_channel_id(self._pon_id, onu_id)
Chip Boling252c7772017-08-16 10:13:17 -0500814 }
Chip Bolingae298012017-08-28 08:55:17 -0500815 # Hold off ONU activation until at least one GEM Port is defined.
816
817 return onu_info if len(gem_ports) > 0 else None
Chip Boling252c7772017-08-16 10:13:17 -0500818
819 except Exception as e:
820 self.log.exception('get-onu-info', e=e)
821 return None
822
Chip Boling3e3b1a92017-05-16 11:51:18 -0500823 @inlineCallbacks
824 def add_onu(self, serial_number, status):
Chip Boling27275992017-09-22 15:17:04 -0500825 self.log.info('add-onu', serial_number=serial_number, status=status)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500826
827 if serial_number not in status.onus:
828 # Newly found and not enabled ONU, enable it now if not at max
829
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500830 onu_info = self._get_onu_info(Onu.serial_number_to_string(serial_number))
831
832 if onu_info is None:
833 self.log.info('lookup-failure', serial_number=serial_number)
834
835 elif serial_number in self._onus or onu_info['onu-id'] in self._onu_by_id:
836 # May be here due to unmanaged power-cycle on OLT
837 self.log.info('onu-already-added', serial_number=serial_number)
838 assert serial_number in self._onus and\
839 onu_info['onu-id'] in self._onu_by_id, \
840 'ONU not in both lists'
841
842 # Recover ONU information and attempt to reflow TCONT/GEM-PORT
843 # information as well
844
845 onu = self._onus[serial_number]
846 reflow = True
847
848 elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
849 self.log.warning('max-onus-provisioned', count=len(self._onus))
Chip Boling252c7772017-08-16 10:13:17 -0500850 else:
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500851 # TODO: Make use of upstream_channel_speed variable
852 onu = Onu(onu_info)
853 reflow = False
854 self._onus[serial_number] = onu
855 self._onu_by_id[onu.onu_id] = onu
Chip Boling3e3b1a92017-05-16 11:51:18 -0500856
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500857 try:
858 tconts = onu_info['t-conts']
859 gem_ports = onu_info['gem-ports']
Chip Boling252c7772017-08-16 10:13:17 -0500860
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500861 # Add Multicast to PON on a per-ONU basis until xPON multicast support is ready
862 # In xPON/BBF, mcast gems tie back to the channel-pair
863 # MCAST VLAN IDs stored as a negative value
Chip Boling252c7772017-08-16 10:13:17 -0500864
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500865 for id_or_vid, gem_port in gem_ports.iteritems(): # TODO: Deprecate this when BBF ready
866 if gem_port.multicast:
867 self.add_mcast_gem_port(gem_port, -id_or_vid)
Chip Boling252c7772017-08-16 10:13:17 -0500868
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500869 yield onu.create(tconts, gem_ports, reflow=reflow)
870 if not reflow:
871 self.activate_onu(onu)
Chip Boling27275992017-09-22 15:17:04 -0500872
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500873 except Exception as e:
874 self.log.exception('add-onu', serial_number=serial_number, reflow=reflow, e=e)
Chip Boling27275992017-09-22 15:17:04 -0500875
Chip Bolingdb61c6a2017-09-27 12:36:45 -0500876 if not reflow:
877 del self._onus[serial_number]
878 del self._onu_by_id[onu.onu_id]
Chip Boling3e3b1a92017-05-16 11:51:18 -0500879
Chip Boling252c7772017-08-16 10:13:17 -0500880 def activate_onu(self, onu):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500881 """
882 Called when a new ONU is discovered and VOLTHA device adapter needs to be informed
883 :param onu:
884 :return:
885 """
Chip Boling61a12792017-10-02 13:23:27 -0500886 # Only call older 'child_device_detected' if not using xPON to configure the system
Chip Boling3e3b1a92017-05-16 11:51:18 -0500887
Chip Boling61a12792017-10-02 13:23:27 -0500888 if self.activation_method == "autoactivate":
889 olt = self.olt
890 adapter = self.adapter_agent
891 channel_id = onu.onu_vid
Chip Boling3e3b1a92017-05-16 11:51:18 -0500892
Chip Boling61a12792017-10-02 13:23:27 -0500893 proxy = Device.ProxyAddress(device_id=olt.device_id,
894 channel_id=channel_id,
895 onu_id=onu.onu_id,
896 onu_session_id=onu.onu_id)
897
898 adapter.child_device_detected(parent_device_id=olt.device_id,
899 parent_port_no=self._port_no,
900 child_device_type=onu.vendor_id,
901 proxy_address=proxy,
902 admin_state=AdminState.ENABLED,
903 vlan=channel_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500904
905 def get_next_onu_id(self):
Chip Boling5561d552017-07-07 15:11:26 -0500906 used_ids = [onu.onu_id for onu in self._onus.itervalues()]
Chip Boling3e3b1a92017-05-16 11:51:18 -0500907
908 while True:
Chip Boling5561d552017-07-07 15:11:26 -0500909 onu_id = self._next_onu_id
910 self._next_onu_id += 1
Chip Boling3e3b1a92017-05-16 11:51:18 -0500911
Chip Boling5561d552017-07-07 15:11:26 -0500912 if self._next_onu_id > Onu.MAX_ONU_ID:
Chip Boling27275992017-09-22 15:17:04 -0500913 self._next_onu_id = Onu.MIN_ONU_ID + 128
Chip Boling3e3b1a92017-05-16 11:51:18 -0500914
915 if onu_id not in used_ids:
916 return onu_id
917
Chip Bolingae298012017-08-28 08:55:17 -0500918 @inlineCallbacks
Chip Boling3e3b1a92017-05-16 11:51:18 -0500919 def delete_onu(self, onu_id):
Chip Boling252c7772017-08-16 10:13:17 -0500920 uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, onu_id)
Chip Boling7294b252017-06-15 16:16:55 -0500921 name = 'pon-delete-onu-{}-{}'.format(self._pon_id, onu_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500922
Chip Bolingae298012017-08-28 08:55:17 -0500923 onu = self._onu_by_id.get(onu_id)
924
Chip Boling252c7772017-08-16 10:13:17 -0500925 # Remove from any local dictionary
926 if onu_id in self._onu_by_id:
927 del self._onu_by_id[onu_id]
928 for sn in [onu.serial_numbers for onu in self._onus.itervalues() if onu.onu_id == onu_id]:
929 del self._onus[sn]
Chip Bolingae298012017-08-28 08:55:17 -0500930 try:
931 yield self._parent.rest_client.request('DELETE', uri, name=name)
932
933 except Exception as e:
934 self.log.exception('onu', serial_number=onu.serial_number, e=e)
935
Chip Boling27275992017-09-22 15:17:04 -0500936 if onu is not None:
937 # Clean up adapter agent of this ONU
Chip Bolingae298012017-08-28 08:55:17 -0500938
Chip Boling27275992017-09-22 15:17:04 -0500939 proxy = Device.ProxyAddress(device_id=self.olt.device_id,
940 channel_id=onu.channel_id)
941 onu_device = self.olt.adapter_agent.get_child_device_with_proxy_address(proxy)
Chip Boling252c7772017-08-16 10:13:17 -0500942
Chip Boling27275992017-09-22 15:17:04 -0500943 if onu_device is not None:
944 self.olt.adapter_agent.delete_child_device(self.olt.device_id,
945 onu_device.device_id)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500946
Chip Boling27275992017-09-22 15:17:04 -0500947 self.olt.adapter_agent.update_child_devices_state(self.olt.device_id,
948 admin_state=AdminState.DISABLED)
Chip Bolingae298012017-08-28 08:55:17 -0500949
Chip Boling27275992017-09-22 15:17:04 -0500950 def delete_child_device(self, parent_device_id, child_device_id):
951 onu_device = self.root_proxy.get('/devices/{}'.format(child_device_id))
952 if onu_device is not None:
953 if onu_device.parent_id == parent_device_id:
954 self.log.debug('deleting-child-device', parent_device_id=parent_device_id,
955 child_device_id=child_device_id)
956 self._remove_node('/devices', child_device_id)
957
958 def add_mcast_gem_port(self, mcast_gem, vlan):
Chip Bolingae298012017-08-28 08:55:17 -0500959 """
Chip Boling27275992017-09-22 15:17:04 -0500960 Add any new Multicast GEM Ports to the PON
961 :param mcast_gem: (GemPort)
Chip Bolingae298012017-08-28 08:55:17 -0500962 """
Chip Boling27275992017-09-22 15:17:04 -0500963 if vlan in self._mcast_gem_ports:
964 return
965
966 assert len(self._mcast_gem_ports) == 0, 'Only 1 MCAST GEMPort until BBF Support'
967 assert 1 <= vlan <= 4095, 'Invalid Multicast VLAN ID'
968 assert len(self.olt.multicast_vlans) == 1, 'Only support 1 MCAST VLAN until BBF Support'
969
970 self._mcast_gem_ports[vlan] = mcast_gem
Chip Boling252c7772017-08-16 10:13:17 -0500971
972 @inlineCallbacks
973 def channel_partition(self, name, partition=0, xpon_system=0, operation=None):
974 """
975 Delete/enable/disable a specified channel partition on this PON.
976
977 When creating a new Channel Partition, create it disabled, then define any associated
978 Channel Pairs. Then enable the Channel Partition.
979
980 :param name: (string) Name of the channel partition
981 :param partition: (int: 0..15) An index of the operator-specified channel subset
982 in a NG-PON2 system. For XGS-PON, this is typically 0
983 :param xpon_system: (int: 0..1048575) Identifies a specific xPON system
984 :param operation: (string) 'delete', 'enable', or 'disable'
985 """
986 if operation.lower() not in ['delete', 'enable', 'disable']:
987 raise ValueError('Unsupported operation: {}'.format(operation))
988
989 try:
990 xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
991
992 if operation.lower() is 'delete':
993 xml += '<interface operation="delete">'
994 else:
995 xml += '<interface>'
996 xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
997 'adtn-xp:xpon-channel-partition</type>'
998 xml += '<adtn-xp:channel-partition xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
999 xml += ' <adtn-xp:partition-id>{}</adtn-xp:partition-id>'.format(partition)
1000 xml += ' <adtn-xp:xpon-system>{}</adtn-xp:xpon-system>'.format(xpon_system)
1001 xml += '</adtn-xp:channel-partition>'
1002 xml += '<enabled>{}</enabled>'.format('true' if operation.lower() == 'enable' else 'false')
1003
1004 xml += '<name>{}</name>'.format(name)
1005 xml += '</interface></interfaces>'
1006
1007 results = yield self.olt.netconf_client.edit_config(xml)
1008 returnValue(results)
1009
1010 except Exception as e:
1011 self.log.exception('channel_partition')
1012 raise
1013
1014 @inlineCallbacks
1015 def channel_pair(self, name, partition, operation=None, **kwargs):
1016 """
1017 Create/delete a channel pair on a specific channel_partition for a PON
1018
1019 :param name: (string) Name of the channel pair
1020 :param partition: (string) Name of the channel partition
1021 :param operation: (string) 'delete', 'enable', or 'disable'
1022 :param kwargs: (dict) Additional leaf settings if desired
1023 """
1024 if operation.lower() not in ['delete', 'enable', 'disable']:
1025 raise ValueError('Unsupported operation: {}'.format(operation))
1026
1027 try:
1028 xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"'
1029
1030 if operation.lower() is 'delete':
1031 xml += '<interface operation="delete">'
1032 else:
1033 xml += '<interface>'
1034 xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\
1035 'adtn-xp:xpon-channel-pair</type>'
1036 xml += '<adtn-xp:channel-pair xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">'
1037 xml += ' <adtn-xp:channel-partition>{}</adtn-xp:channel-partition>'.format(partition)
1038 xml += ' <adtn-xp:channel-termination>channel-termination {}</adtn-xp:channel-termination>'.\
1039 format(self.pon_id)
1040 xml += ' <adtn-xp:upstream-admin-label>{}</adtn-xp:upstream-admin-label>'.\
1041 format(kwargs.get('upstream-admin-label', 1))
1042 xml += ' <adtn-xp:downstream-admin-label>{}</adtn-xp:downstream-admin-label>'.\
1043 format(kwargs.get('downstream-admin-label', 1))
1044 xml += ' <adtn-xp:upstream-channel-id>{}</adtn-xp:upstream-channel-id>'.\
1045 format(kwargs.get('upstream-channel-id', 15))
1046 xml += ' <adtn-xp:downstream-channel-id>{}</adtn-xp:downstream-channel-id>'.\
1047 format(kwargs.get('downstream-channel-id', 15))
1048 xml += ' <adtn-xp:downstream-channel-fec-enable>{}</adtn-xp:downstream-channel-fec-enable>'. \
1049 format('true' if kwargs.get('downstream-channel-fec-enable', True) else 'false')
1050 xml += ' <adtn-xp:upstream-channel-fec-enable>{}</adtn-xp:upstream-channel-fec-enable>'. \
1051 format('true' if kwargs.get('upstream-channel-fec-enable', True) else 'false')
1052 xml += '</adtn-xp:channel-pair>'
1053 # TODO: Add support for upstream/downstream FEC-enable coming from here and not hard-coded
1054
1055 xml += '<name>{}</name>'.format(name)
1056 xml += '</interface></interfaces>'
1057
1058 results = yield self.olt.netconf_client.edit_config(xml)
1059 returnValue(results)
1060
1061 except Exception as e:
1062 self.log.exception('channel_pair')
1063 raise