Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 1 | # Copyright 2017-present Open Networking Foundation |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 2 | # |
| 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 Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 8 | # |
| 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 Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 14 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 15 | import json |
| 16 | import pprint |
| 17 | import random |
| 18 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 19 | import structlog |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 20 | from enum import Enum |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 21 | from twisted.internet import reactor |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 22 | from twisted.internet.defer import inlineCallbacks, returnValue, succeed |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 23 | |
| 24 | from adtran_olt_handler import AdtranOltHandler |
| 25 | from codec.olt_config import OltConfig |
| 26 | from onu import Onu |
| 27 | from voltha.protos.common_pb2 import OperStatus, AdminState |
| 28 | from voltha.protos.device_pb2 import Device |
| 29 | from voltha.protos.device_pb2 import Port |
| 30 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 31 | |
| 32 | class PonPort(object): |
| 33 | """ |
| 34 | A class similar to the 'Port' class in the VOLTHA |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 35 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 36 | TODO: Merge this with the Port class or cleanup where possible |
| 37 | so we do not duplicate fields/properties/methods |
| 38 | """ |
| 39 | MAX_ONUS_SUPPORTED = 256 |
| 40 | DEFAULT_ENABLED = False |
| 41 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 42 | class State(Enum): |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 43 | INITIAL = 0 # Created and initialization in progress |
| 44 | RUNNING = 1 # PON port contacted, ONU discovery active |
| 45 | STOPPED = 2 # Disabled |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 46 | DELETING = 3 # Cleanup |
| 47 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 48 | _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery', 'autoactivate'] |
| 49 | _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number'] |
| 50 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 51 | def __init__(self, pon_index, port_no, parent, admin_state=AdminState.UNKNOWN, label=None): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 52 | # TODO: Weed out those properties supported by common 'Port' object (future) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 53 | assert admin_state != AdminState.UNKNOWN |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 54 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 55 | self.log = structlog.get_logger(device_id=parent.device_id, pon_id=pon_index) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 56 | |
| 57 | self._parent = parent |
| 58 | self._pon_id = pon_index |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 59 | self._port_no = port_no |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 60 | self._name = 'xpon 0/{}'.format(pon_index+1) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 61 | self._label = label or 'PON-{}'.format(pon_index) |
| 62 | self._port = None |
| 63 | self._no_onu_discover_tick = 5.0 # TODO: Decrease to 1 or 2 later |
| 64 | self._discovery_tick = 20.0 |
| 65 | self._discovered_onus = [] # List of serial numbers |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 66 | self._onus = {} # serial_number-base64 -> ONU (allowed list) |
| 67 | self._onu_by_id = {} # onu-id -> ONU |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 68 | self._next_onu_id = Onu.MIN_ONU_ID |
| 69 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 70 | self._admin_state = AdminState.DISABLED |
| 71 | self._oper_status = OperStatus.DISCOVERED |
| 72 | self._deferred = None # General purpose |
| 73 | self._discovery_deferred = None # Specifically for ONU discovery |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 74 | self._state = PonPort.State.INITIAL |
| 75 | |
| 76 | # Local cache of PON configuration |
| 77 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 78 | self._xpon_name = None |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 79 | self._enabled = None |
| 80 | self._downstream_fec_enable = None |
| 81 | self._upstream_fec_enable = None |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 82 | self._authentication_method = 'serial-number' |
| 83 | self._activation_method = 'autoactivate' if self.olt.autoactivate else 'autodiscovery' |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 84 | |
| 85 | def __del__(self): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 86 | self.stop() |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 87 | |
| 88 | def __str__(self): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 89 | return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label, |
| 90 | self._admin_state, |
| 91 | self._oper_status, |
| 92 | self.olt) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 93 | |
| 94 | def get_port(self): |
| 95 | """ |
| 96 | Get the VOLTHA PORT object for this port |
| 97 | :return: VOLTHA Port object |
| 98 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 99 | if self._port is None: |
| 100 | self._port = Port(port_no=self._port_no, |
| 101 | label=self._label, |
| 102 | type=Port.PON_OLT, |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 103 | admin_state=AdminState.ENABLED, |
| 104 | oper_status=OperStatus.ACTIVE) |
| 105 | # TODO: For now, no way to report the proper ADMIN or OPER status |
| 106 | # admin_state=self._admin_state, |
| 107 | # oper_status=self._oper_status) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 108 | return self._port |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 109 | |
| 110 | @property |
| 111 | def port_number(self): |
| 112 | return self._port_no |
| 113 | |
| 114 | @property |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 115 | def name(self): |
| 116 | return self._name |
| 117 | |
| 118 | @property |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 119 | def xpon_name(self): |
| 120 | return self._xpon_name |
| 121 | |
| 122 | @xpon_name.setter |
| 123 | def xpon_name(self, value): |
| 124 | self._xpon_name = value |
| 125 | |
| 126 | @property |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 127 | def pon_id(self): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 128 | return self._pon_id |
| 129 | |
| 130 | @property |
| 131 | def olt(self): |
| 132 | return self._parent |
| 133 | |
| 134 | @property |
| 135 | def state(self): |
| 136 | return self._state |
| 137 | |
| 138 | @property |
| 139 | def adapter_agent(self): |
| 140 | return self.olt.adapter_agent |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 141 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 142 | @property |
| 143 | def discovery_tick(self): |
| 144 | return self._discovery_tick * 10 |
| 145 | |
| 146 | @discovery_tick.setter |
| 147 | def discovery_tick(self, value): |
| 148 | if value < 0: |
| 149 | raise ValueError("Polling interval must be >= 0") |
| 150 | |
| 151 | if self.discovery_tick != value: |
| 152 | self._discovery_tick = value / 10 |
| 153 | |
| 154 | if self._discovery_deferred is not None: |
| 155 | self._discovery_deferred.cancel() |
| 156 | self._discovery_deferred = None |
| 157 | |
| 158 | if self._discovery_tick > 0: |
| 159 | self._discovery_deferred = reactor.callLater(self._discovery_tick, |
| 160 | self._discover_onus) |
| 161 | |
| 162 | @property |
| 163 | def activation_method(self): |
| 164 | return self._activation_method |
| 165 | |
| 166 | @activation_method.setter |
| 167 | def activation_method(self, value): |
| 168 | value = value.lower() |
| 169 | if value not in PonPort._SUPPORTED_ACTIVATION_METHODS: |
| 170 | raise ValueError('Invalid ONU activation method') |
| 171 | self._activation_method = value |
| 172 | |
| 173 | @property |
| 174 | def authentication_method(self): |
| 175 | return self._authentication_method |
| 176 | |
| 177 | @authentication_method.setter |
| 178 | def authentication_method(self, value): |
| 179 | value = value.lower() |
| 180 | if value not in PonPort._SUPPORTED_AUTHENTICATION_METHODS: |
| 181 | raise ValueError('Invalid ONU authentication method') |
| 182 | self._authentication_method = value |
| 183 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 184 | def get_logical_port(self): |
| 185 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 186 | Get the VOLTHA logical port for this port. For PON ports, a logical port |
| 187 | is not currently created, so always return None |
| 188 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 189 | :return: VOLTHA logical port or None if not supported |
| 190 | """ |
| 191 | return None |
| 192 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 193 | def _cancel_deferred(self): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 194 | d1, self._deferred = self._deferred, None |
| 195 | d2, self._discovery_deferred = self._discovery_deferred, None |
| 196 | |
| 197 | if d1 is not None: |
| 198 | d1.cancel() |
| 199 | if d2 is not None: |
| 200 | d2.cancel() |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 201 | |
| 202 | def _update_adapter_agent(self): |
| 203 | # TODO: Currently the adapter_agent does not allow 'update' of port status |
| 204 | # self.adapter_agent.update_port(self.olt.device_id, self.get_port()) |
| 205 | pass |
| 206 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 207 | def start(self): |
| 208 | """ |
| 209 | Start/enable this PON and start ONU discover |
| 210 | :return: (deferred) |
| 211 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 212 | if self._state == PonPort.State.RUNNING: |
| 213 | return succeed('Running') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 214 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 215 | self.log.info('start') |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 216 | |
| 217 | self._cancel_deferred() |
| 218 | self._state = PonPort.State.INITIAL |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 219 | self._oper_status = OperStatus.ACTIVATING |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 220 | |
| 221 | # Do the rest of the startup in an async method |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 222 | self._deferred = reactor.callLater(0.5, self._finish_startup) |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 223 | self._update_adapter_agent() |
| 224 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 225 | return self._deferred |
| 226 | |
| 227 | @inlineCallbacks |
| 228 | def _finish_startup(self): |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 229 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 230 | Do all startup offline since REST may fail |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 231 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 232 | if self._state != PonPort.State.INITIAL: |
| 233 | returnValue('Done') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 234 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 235 | self.log.debug('final-startup') |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 236 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 237 | if self._enabled is None or self._downstream_fec_enable is None or self._upstream_fec_enable is None: |
| 238 | try: |
| 239 | self._deferred = self.get_pon_config() |
| 240 | results = yield self._deferred |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 241 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 242 | except Exception as e: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 243 | self.log.exception('initial-GET', e=e) |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 244 | self._deferred = reactor.callLater(5, self._finish_startup) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 245 | returnValue(self._deferred) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 246 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 247 | # Load cache |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 248 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 249 | self._enabled = results.get('enabled', False) |
| 250 | self._downstream_fec_enable = results.get('downstream-fec-enable', False) |
| 251 | self._upstream_fec_enable = results.get('upstream-fec-enable', False) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 252 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 253 | if not self._enabled: |
| 254 | try: |
| 255 | self._deferred = self.set_pon_config("enabled", True) |
| 256 | results = yield self._deferred |
| 257 | self._enabled = True |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 258 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 259 | except Exception as e: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 260 | self.log.exception('final-startup-enable', e=e) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 261 | self._deferred = reactor.callLater(3, self._finish_startup) |
| 262 | returnValue(self._deferred) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 263 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 264 | if not self._downstream_fec_enable: |
| 265 | try: |
| 266 | self._deferred = self.set_pon_config("downstream-fec-enable", True) |
| 267 | results = yield self._deferred |
| 268 | self._downstream_fec_enable = True |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 269 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 270 | except Exception as e: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 271 | self.log.exception('final-startup-downstream-FEC', e=e) |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 272 | self._deferred = reactor.callLater(5, self._finish_startup) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 273 | returnValue(self._deferred) |
| 274 | |
| 275 | if not self._upstream_fec_enable: |
| 276 | try: |
| 277 | self._deferred = self.set_pon_config("upstream-fec-enable", True) |
| 278 | results = yield self._deferred |
| 279 | self._upstream_fec_enable = True |
| 280 | |
| 281 | except Exception as e: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 282 | self.log.exception('final-startup-upstream-FEC', e=e) |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 283 | self._deferred = reactor.callLater(5, self._finish_startup) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 284 | returnValue(self._deferred) |
| 285 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 286 | self.log.debug('startup-complete', results=pprint.PrettyPrinter().pformat(results)) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 287 | |
| 288 | if self._enabled: |
| 289 | self._admin_state = AdminState.ENABLED |
| 290 | self._oper_status = OperStatus.ACTIVE # TODO: is this correct, how do we tell GRPC |
| 291 | self._state = PonPort.State.RUNNING |
| 292 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 293 | # Begin to ONU discovery |
| 294 | |
| 295 | self._discovery_deferred = reactor.callLater(5, self._discover_onus) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 296 | |
| 297 | self._update_adapter_agent() |
| 298 | returnValue('Enabled') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 299 | |
| 300 | else: |
| 301 | # Startup failed. Could be due to object creation with an invalid initial admin_status |
| 302 | # state. May want to schedule a start to occur again if this happens |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 303 | self._admin_state = AdminState.DISABLED |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 304 | self._oper_status = OperStatus.FAILED |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 305 | self._state = PonPort.State.STOPPED |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 306 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 307 | self._update_adapter_agent() |
| 308 | returnValue('Disabled') |
| 309 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 310 | def stop(self): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 311 | if self._state == PonPort.State.STOPPED: |
| 312 | return succeed('Stopped') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 313 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 314 | self.log.info('stopping') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 315 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 316 | self._cancel_deferred() |
| 317 | self._deferred = self.set_pon_config("enabled", False) |
| 318 | |
| 319 | # Flush config cache |
| 320 | self._enabled = None |
| 321 | self._downstream_fec_enable = None |
| 322 | self._upstream_fec_enable = None |
| 323 | |
| 324 | self._admin_state = AdminState.DISABLED |
| 325 | self._oper_status = OperStatus.UNKNOWN |
| 326 | self._update_adapter_agent() |
| 327 | |
| 328 | self._state = PonPort.State.STOPPED |
| 329 | return self._deferred |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 330 | |
| 331 | @inlineCallbacks |
| 332 | def reset(self): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 333 | """ |
| 334 | Set the PON Port to a known good state on initial port startup. Actual |
| 335 | PON 'Start' is done elsewhere |
| 336 | """ |
| 337 | if self._state != PonPort.State.INITIAL: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 338 | self.log.error('reset-ignored', state=self._state) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 339 | returnValue('Ignored') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 340 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 341 | self.log.info('reset') |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 342 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 343 | try: |
| 344 | self._deferred = self.get_pon_config() |
| 345 | results = yield self._deferred |
| 346 | |
| 347 | # Load cache |
| 348 | self._enabled = results.get('enabled', False) |
| 349 | |
| 350 | except Exception as e: |
| 351 | self._enabled = None |
| 352 | self.log.exception('GET-failed', e=e) |
| 353 | |
| 354 | initial_port_state = AdminState.ENABLED if self.olt.autoactivate else AdminState.DISABLED |
| 355 | |
| 356 | if self._admin_state != initial_port_state: |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 357 | try: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 358 | enable = initial_port_state == AdminState.ENABLED |
| 359 | if self._enabled is None or self._enabled != enable: |
| 360 | yield self.set_pon_config("enabled", enable) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 361 | |
| 362 | # TODO: Move to 'set_pon_config' method and also make sure GRPC/Port is ok |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 363 | self._admin_state = AdminState.ENABLED if enable else AdminState.DISABLED |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 364 | |
| 365 | except Exception as e: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 366 | self.log.exception('reset', e=e) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 367 | raise |
| 368 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 369 | # Walk the provisioned ONU list and disable any exiting ONUs |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 370 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 371 | try: |
| 372 | results = yield self.get_onu_config() |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 373 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 374 | if isinstance(results, list) and len(results) > 0: |
| 375 | onu_configs = OltConfig.Pon.Onu.decode(results) |
| 376 | for onu_id in onu_configs.iterkeys(): |
| 377 | try: |
| 378 | yield self.delete_onu(onu_id) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 379 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 380 | except Exception as e: |
| 381 | self.log.exception('rest-ONU-delete', onu_id=onu_id, e=e) |
| 382 | pass # Non-fatal |
| 383 | |
| 384 | except Exception as e: |
| 385 | self.log.exception('onu-delete', e=e) |
| 386 | raise |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 387 | |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 388 | returnValue('Reset complete') |
| 389 | |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 390 | def restart(self): |
| 391 | if self._state == PonPort.State.RUNNING or self._state == PonPort.State.STOPPED: |
| 392 | start_it = (self._state == PonPort.State.RUNNING) |
| 393 | self._state = PonPort.State.INITIAL |
| 394 | return self.start() if start_it else self.stop() |
| 395 | return succeed('nop') |
| 396 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 397 | def delete(self): |
| 398 | """ |
| 399 | Parent device is being deleted. Do not change any config but |
| 400 | stop all polling |
| 401 | """ |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 402 | self.log.info('Deleting') |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 403 | self._state = PonPort.State.DELETING |
| 404 | self._cancel_deferred() |
| 405 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 406 | # @property |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 407 | def gem_ids(self, onu_vid, exception_gems): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 408 | """ |
| 409 | Get all GEM Port IDs used on a given PON |
| 410 | |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 411 | :param onu_vid: (int) ONU VLAN ID if customer ONU specific. None if for all ONUs |
| 412 | on PON |
| 413 | :param exception_gems: (boolean) Select from special purpose ACL GEM-Portas |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 414 | :return: (dict) key -> onu-id, value -> frozenset of GEM Port IDs |
| 415 | """ |
| 416 | gem_ids = {} |
| 417 | for onu_id, onu in self._onu_by_id.iteritems(): |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 418 | if onu_vid is None or onu_vid == onu.onu_vid: |
| 419 | gem_ids[onu_id] = onu.gem_ids(exception_gems) |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 420 | return gem_ids |
| 421 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 422 | def get_pon_config(self): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 423 | uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id) |
| 424 | name = 'pon-get-config-{}'.format(self._pon_id) |
| 425 | return self._parent.rest_client.request('GET', uri, name=name) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 426 | |
| 427 | def get_onu_config(self, onu_id=None): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 428 | if onu_id is None: |
| 429 | uri = AdtranOltHandler.GPON_ONU_CONFIG_LIST_URI.format(self._pon_id) |
| 430 | else: |
| 431 | uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, onu_id) |
| 432 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 433 | name = 'pon-get-onu_config-{}-{}'.format(self._pon_id, onu_id) |
| 434 | return self._parent.rest_client.request('GET', uri, name=name) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 435 | |
| 436 | def set_pon_config(self, leaf, value): |
| 437 | data = json.dumps({leaf: value}) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 438 | uri = AdtranOltHandler.GPON_PON_CONFIG_URI.format(self._pon_id) |
| 439 | name = 'pon-set-config-{}-{}-{}'.format(self._pon_id, leaf, str(value)) |
| 440 | return self._parent.rest_client.request('PATCH', uri, data=data, name=name) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 441 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 442 | def _discover_onus(self): |
| 443 | self.log.debug('discovery') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 444 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 445 | if self._admin_state == AdminState.ENABLED: |
| 446 | data = json.dumps({'pon-id': self._pon_id}) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 447 | uri = AdtranOltHandler.GPON_PON_DISCOVER_ONU |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 448 | name = 'pon-discover-onu-{}'.format(self._pon_id) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 449 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 450 | self._discovery_deferred = self._parent.rest_client.request('POST', uri, data, name=name) |
| 451 | self._discovery_deferred.addBoth(self._onu_discovery_init_complete) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 452 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 453 | def _onu_discovery_init_complete(self, _): |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 454 | """ |
| 455 | This method is called after the REST POST to request ONU discovery is |
| 456 | completed. The results (body) of the post is always empty / 204 NO CONTENT |
| 457 | """ |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 458 | # Reschedule |
| 459 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 460 | delay = self._no_onu_discover_tick if len(self._onus) == 0 else self._discovery_tick |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 461 | delay += random.uniform(-delay / 10, delay / 10) |
| 462 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 463 | self._discovery_deferred = reactor.callLater(delay, self._discover_onus) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 464 | |
| 465 | def process_status_poll(self, status): |
| 466 | """ |
| 467 | Process PON status poll request |
| 468 | |
| 469 | :param status: (OltState.Pon object) results from RESTCONF GET |
| 470 | """ |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 471 | self.log.debug('process-status-poll', status=status) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 472 | |
| 473 | if self._admin_state != AdminState.ENABLED: |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 474 | return |
| 475 | |
| 476 | # Process the ONU list in for this PON, may have previously provisioned ones there |
| 477 | # were discovered on an earlier boot |
| 478 | |
| 479 | new = self._process_status_onu_list(status.onus) |
| 480 | |
| 481 | for onu_id in new: |
| 482 | # self.add_new_onu(serial_number, status) |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 483 | self.log.info('found-ONU', onu_id=onu_id) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 484 | raise NotImplementedError('TODO: Adding ONUs from existing ONU (status list) not supported') |
| 485 | |
| 486 | # Get new/missing from the discovered ONU leaf |
| 487 | |
| 488 | new, missing = self._process_status_onu_discovered_list(status.discovered_onu) |
| 489 | |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 490 | # TODO: Do something useful (Does the discovery list clear out activated ONU's?) |
| 491 | # if len(missing): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 492 | # self.log.info('missing-ONUs', missing=missing) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 493 | |
| 494 | for serial_number in new: |
| 495 | reactor.callLater(0, self.add_onu, serial_number, status) |
| 496 | |
| 497 | # Process discovered ONU list |
| 498 | |
| 499 | # TODO: Process LOS list |
| 500 | # TODO: Process status |
| 501 | pass |
| 502 | |
| 503 | def _process_status_onu_list(self, onus): |
| 504 | """ |
| 505 | Look for new or missing ONUs |
| 506 | |
| 507 | :param onus: (dict) Set of known ONUs |
| 508 | """ |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 509 | self.log.debug('ONU-list', onus=onus) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 510 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 511 | my_onu_ids = frozenset([o.onu_id for o in self._onus.itervalues()]) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 512 | discovered_onus = frozenset(onus.keys()) |
| 513 | |
| 514 | new_onus_ids = discovered_onus - my_onu_ids |
| 515 | missing_onus_ids = my_onu_ids - discovered_onus |
| 516 | |
| 517 | new = {o: v for o, v in onus.iteritems() if o in new_onus_ids} |
| 518 | missing_onus = {o: v for o, v in onus.iteritems() if o in missing_onus_ids} |
| 519 | |
| 520 | return new # , missing_onus # TODO: Support ONU removal |
| 521 | |
| 522 | def _process_status_onu_discovered_list(self, discovered_onus): |
| 523 | """ |
| 524 | Look for new or missing ONUs |
| 525 | |
| 526 | :param discovered_onus: (frozenset) Set of ONUs currently discovered |
| 527 | """ |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 528 | self.log.debug('discovered-ONUs', list=discovered_onus) |
| 529 | |
| 530 | # Only request discovery if activation is auto-discovery or auto-activate |
| 531 | continue_discovery = ['autodiscovery', 'autoactivate'] |
| 532 | |
| 533 | if self._activation_method not in continue_discovery: |
| 534 | return set(), set() |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 535 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 536 | my_onus = frozenset(self._onus.keys()) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 537 | |
| 538 | new_onus = discovered_onus - my_onus |
| 539 | missing_onus = my_onus - discovered_onus |
| 540 | |
| 541 | return new_onus, missing_onus |
| 542 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 543 | def _get_onu_info(self, serial_number): |
| 544 | """ |
| 545 | Parse through available xPON information for ONU configuration settings |
| 546 | :param serial_number: (string) Decoded (not base64) serial number string |
| 547 | :return: (dict) onu config data or None on lookup failure |
| 548 | """ |
| 549 | try: |
| 550 | from flow.demo_data import get_tconts, get_gem_ports |
| 551 | |
| 552 | if self.activation_method == "autoactivate": |
| 553 | onu_id = self.get_next_onu_id() |
| 554 | enabled = True |
| 555 | channel_speed = 0 |
| 556 | |
| 557 | elif self.activation_method == "autodiscovery": |
| 558 | if self.authentication_method == 'serial-number': |
| 559 | gpon_info = self.olt.get_xpon_info(self.pon_id) |
| 560 | |
| 561 | try: |
| 562 | vont_info = next(info for _, info in gpon_info['v_ont_anis'].items() |
| 563 | if info.get('expected-serial-number') == serial_number) |
| 564 | |
| 565 | onu_id = vont_info['onu-id'] |
| 566 | enabled = vont_info['enabled'] |
| 567 | channel_speed = vont_info['upstream-channel-speed'] |
| 568 | |
| 569 | except StopIteration: |
| 570 | return None |
| 571 | else: |
| 572 | return None |
| 573 | else: |
| 574 | return None |
| 575 | |
| 576 | onu_info = { |
| 577 | 'serial-number': serial_number, |
| 578 | 'xpon-name': None, |
| 579 | 'pon': self, |
| 580 | 'onu-id': onu_id, |
| 581 | 'enabled': enabled, |
| 582 | 'upstream-channel-speed': channel_speed, |
| 583 | 'password': Onu.DEFAULT_PASSWORD, |
| 584 | 't-conts': get_tconts(self.pon_id, serial_number, onu_id), |
| 585 | 'gem-ports': get_gem_ports(self.pon_id, serial_number, onu_id), |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 586 | 'onu-vid': self.olt.get_channel_id(self._pon_id, onu_id) |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 587 | } |
| 588 | return onu_info |
| 589 | |
| 590 | except Exception as e: |
| 591 | self.log.exception('get-onu-info', e=e) |
| 592 | return None |
| 593 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 594 | @inlineCallbacks |
| 595 | def add_onu(self, serial_number, status): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 596 | self.log.info('add-ONU', serial_number=serial_number) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 597 | |
| 598 | if serial_number not in status.onus: |
| 599 | # Newly found and not enabled ONU, enable it now if not at max |
| 600 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 601 | if len(self._onus) >= self.MAX_ONUS_SUPPORTED: |
| 602 | self.log.warning('max-onus-provisioned') |
| 603 | else: |
| 604 | onu_info = self._get_onu_info(Onu.serial_number_to_string(serial_number)) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 605 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 606 | if onu_info is None: |
| 607 | self.log.info('lookup-failure', serial_number=serial_number) |
| 608 | |
| 609 | elif serial_number in self._onus or onu_info['onu-id'] in self._onu_by_id: |
| 610 | self.log.warning('onu-already-added', serial_number=serial_number) |
| 611 | |
| 612 | else: |
| 613 | # TODO: Make use of upstream_channel_speed variable |
| 614 | onu = Onu(onu_info) |
| 615 | self._onus[serial_number] = onu |
| 616 | self._onu_by_id[onu.onu_id] = onu |
| 617 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 618 | try: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 619 | yield onu.create(onu_info) |
| 620 | self.activate_onu(onu) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 621 | |
| 622 | except Exception as e: |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 623 | del self._onus[serial_number] |
| 624 | del self._onu_by_id[onu.onu_id] |
| 625 | self.log.exception('add_onu', serial_number=serial_number, e=e) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 626 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 627 | def activate_onu(self, onu): |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 628 | """ |
| 629 | Called when a new ONU is discovered and VOLTHA device adapter needs to be informed |
| 630 | :param onu: |
| 631 | :return: |
| 632 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 633 | olt = self.olt |
| 634 | adapter = self.adapter_agent |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame^] | 635 | channel_id = onu.onu_vid |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 636 | |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 637 | proxy = Device.ProxyAddress(device_id=olt.device_id, channel_id=channel_id) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 638 | |
| 639 | adapter.child_device_detected(parent_device_id=olt.device_id, |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 640 | parent_port_no=self._port_no, |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 641 | child_device_type=onu.vendor_id, |
Nikolay Titov | 89004ec | 2017-06-19 18:22:42 -0400 | [diff] [blame] | 642 | proxy_address=proxy, |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 643 | admin_state=AdminState.ENABLED, |
| 644 | vlan=channel_id) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 645 | |
| 646 | def get_next_onu_id(self): |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 647 | used_ids = [onu.onu_id for onu in self._onus.itervalues()] |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 648 | |
| 649 | while True: |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 650 | onu_id = self._next_onu_id |
| 651 | self._next_onu_id += 1 |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 652 | |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 653 | if self._next_onu_id > Onu.MAX_ONU_ID: |
| 654 | self._next_onu_id = Onu.MIN_ONU_ID |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 655 | |
| 656 | if onu_id not in used_ids: |
| 657 | return onu_id |
| 658 | |
| 659 | def delete_onu(self, onu_id): |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 660 | uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, onu_id) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 661 | name = 'pon-delete-onu-{}-{}'.format(self._pon_id, onu_id) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 662 | |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 663 | # Remove from any local dictionary |
| 664 | if onu_id in self._onu_by_id: |
| 665 | del self._onu_by_id[onu_id] |
| 666 | for sn in [onu.serial_numbers for onu in self._onus.itervalues() if onu.onu_id == onu_id]: |
| 667 | del self._onus[sn] |
| 668 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 669 | # TODO: Need removal from VOLTHA child_device method |
| 670 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 671 | return self._parent.rest_client.request('DELETE', uri, name=name) |
Chip Boling | 252c777 | 2017-08-16 10:13:17 -0500 | [diff] [blame] | 672 | |
| 673 | @inlineCallbacks |
| 674 | def channel_partition(self, name, partition=0, xpon_system=0, operation=None): |
| 675 | """ |
| 676 | Delete/enable/disable a specified channel partition on this PON. |
| 677 | |
| 678 | When creating a new Channel Partition, create it disabled, then define any associated |
| 679 | Channel Pairs. Then enable the Channel Partition. |
| 680 | |
| 681 | :param name: (string) Name of the channel partition |
| 682 | :param partition: (int: 0..15) An index of the operator-specified channel subset |
| 683 | in a NG-PON2 system. For XGS-PON, this is typically 0 |
| 684 | :param xpon_system: (int: 0..1048575) Identifies a specific xPON system |
| 685 | :param operation: (string) 'delete', 'enable', or 'disable' |
| 686 | """ |
| 687 | if operation.lower() not in ['delete', 'enable', 'disable']: |
| 688 | raise ValueError('Unsupported operation: {}'.format(operation)) |
| 689 | |
| 690 | try: |
| 691 | xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"' |
| 692 | |
| 693 | if operation.lower() is 'delete': |
| 694 | xml += '<interface operation="delete">' |
| 695 | else: |
| 696 | xml += '<interface>' |
| 697 | xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\ |
| 698 | 'adtn-xp:xpon-channel-partition</type>' |
| 699 | xml += '<adtn-xp:channel-partition xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' |
| 700 | xml += ' <adtn-xp:partition-id>{}</adtn-xp:partition-id>'.format(partition) |
| 701 | xml += ' <adtn-xp:xpon-system>{}</adtn-xp:xpon-system>'.format(xpon_system) |
| 702 | xml += '</adtn-xp:channel-partition>' |
| 703 | xml += '<enabled>{}</enabled>'.format('true' if operation.lower() == 'enable' else 'false') |
| 704 | |
| 705 | xml += '<name>{}</name>'.format(name) |
| 706 | xml += '</interface></interfaces>' |
| 707 | |
| 708 | results = yield self.olt.netconf_client.edit_config(xml) |
| 709 | returnValue(results) |
| 710 | |
| 711 | except Exception as e: |
| 712 | self.log.exception('channel_partition') |
| 713 | raise |
| 714 | |
| 715 | @inlineCallbacks |
| 716 | def channel_pair(self, name, partition, operation=None, **kwargs): |
| 717 | """ |
| 718 | Create/delete a channel pair on a specific channel_partition for a PON |
| 719 | |
| 720 | :param name: (string) Name of the channel pair |
| 721 | :param partition: (string) Name of the channel partition |
| 722 | :param operation: (string) 'delete', 'enable', or 'disable' |
| 723 | :param kwargs: (dict) Additional leaf settings if desired |
| 724 | """ |
| 725 | if operation.lower() not in ['delete', 'enable', 'disable']: |
| 726 | raise ValueError('Unsupported operation: {}'.format(operation)) |
| 727 | |
| 728 | try: |
| 729 | xml = 'interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"' |
| 730 | |
| 731 | if operation.lower() is 'delete': |
| 732 | xml += '<interface operation="delete">' |
| 733 | else: |
| 734 | xml += '<interface>' |
| 735 | xml += '<type xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' +\ |
| 736 | 'adtn-xp:xpon-channel-pair</type>' |
| 737 | xml += '<adtn-xp:channel-pair xmlns:adtn-xp="http://www.adtran.com/ns/yang/adtran-xpon">' |
| 738 | xml += ' <adtn-xp:channel-partition>{}</adtn-xp:channel-partition>'.format(partition) |
| 739 | xml += ' <adtn-xp:channel-termination>channel-termination {}</adtn-xp:channel-termination>'.\ |
| 740 | format(self.pon_id) |
| 741 | xml += ' <adtn-xp:upstream-admin-label>{}</adtn-xp:upstream-admin-label>'.\ |
| 742 | format(kwargs.get('upstream-admin-label', 1)) |
| 743 | xml += ' <adtn-xp:downstream-admin-label>{}</adtn-xp:downstream-admin-label>'.\ |
| 744 | format(kwargs.get('downstream-admin-label', 1)) |
| 745 | xml += ' <adtn-xp:upstream-channel-id>{}</adtn-xp:upstream-channel-id>'.\ |
| 746 | format(kwargs.get('upstream-channel-id', 15)) |
| 747 | xml += ' <adtn-xp:downstream-channel-id>{}</adtn-xp:downstream-channel-id>'.\ |
| 748 | format(kwargs.get('downstream-channel-id', 15)) |
| 749 | xml += ' <adtn-xp:downstream-channel-fec-enable>{}</adtn-xp:downstream-channel-fec-enable>'. \ |
| 750 | format('true' if kwargs.get('downstream-channel-fec-enable', True) else 'false') |
| 751 | xml += ' <adtn-xp:upstream-channel-fec-enable>{}</adtn-xp:upstream-channel-fec-enable>'. \ |
| 752 | format('true' if kwargs.get('upstream-channel-fec-enable', True) else 'false') |
| 753 | xml += '</adtn-xp:channel-pair>' |
| 754 | # TODO: Add support for upstream/downstream FEC-enable coming from here and not hard-coded |
| 755 | |
| 756 | xml += '<name>{}</name>'.format(name) |
| 757 | xml += '</interface></interfaces>' |
| 758 | |
| 759 | results = yield self.olt.netconf_client.edit_config(xml) |
| 760 | returnValue(results) |
| 761 | |
| 762 | except Exception as e: |
| 763 | self.log.exception('channel_pair') |
| 764 | raise |