Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 1 | # |
| 2 | # Copyright 2017-present Adtran, Inc. |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | # |
| 16 | |
| 17 | import random |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 18 | import arrow |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 19 | |
| 20 | import structlog |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 21 | import xmltodict |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 22 | from port import AdtnPort |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 23 | from twisted.internet import reactor |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 24 | from twisted.internet.defer import inlineCallbacks, returnValue, succeed, fail |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 25 | from twisted.python.failure import Failure |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 26 | from voltha.core.logical_device_agent import mac_str_to_tuple |
| 27 | from voltha.protos.common_pb2 import OperStatus, AdminState |
| 28 | from voltha.protos.device_pb2 import Port |
| 29 | from voltha.protos.logical_device_pb2 import LogicalPort |
| 30 | from voltha.protos.openflow_13_pb2 import OFPPF_100GB_FD, OFPPF_FIBER, OFPPS_LIVE, ofp_port |
| 31 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 32 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 33 | class NniPort(AdtnPort): |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 34 | """ |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 35 | Northbound network port, often Ethernet-based |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 36 | """ |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 37 | def __init__(self, parent, **kwargs): |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 38 | super(NniPort, self).__init__(parent, **kwargs) |
| 39 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 40 | # TODO: Weed out those properties supported by common 'Port' object |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 41 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 42 | self.log = structlog.get_logger(port_no=kwargs.get('port_no')) |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 43 | self.log.info('creating') |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 44 | |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 45 | # ONOS/SEBA wants 'nni-<port>' for port names, OLT NETCONF wants their |
| 46 | # name (something like hundred-gigabit-ethernet 0/1) which is reported |
| 47 | # when we enumerated the ports |
| 48 | self._physical_port_name = kwargs.get('name', 'nni-{}'.format(self._port_no)) |
| 49 | self._logical_port_name = 'nni-{}'.format(self._port_no) |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 50 | self._logical_port = None |
| 51 | |
| 52 | self.sync_tick = 10.0 |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 53 | |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 54 | self._stats_tick = 5.0 |
| 55 | self._stats_deferred = None |
| 56 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 57 | # Local cache of NNI configuration |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 58 | self._ianatype = '<type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>' |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 59 | |
| 60 | # And optional parameters |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 61 | # TODO: Currently cannot update admin/oper status, so create this enabled and active |
| 62 | # self._admin_state = kwargs.pop('admin_state', AdminState.UNKNOWN) |
| 63 | # self._oper_status = kwargs.pop('oper_status', OperStatus.UNKNOWN) |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 64 | self._enabled = True |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 65 | self._admin_state = AdminState.ENABLED |
| 66 | self._oper_status = OperStatus.ACTIVE |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 67 | |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 68 | self._label = self._physical_port_name |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 69 | self._mac_address = kwargs.pop('mac_address', '00:00:00:00:00:00') |
| 70 | # TODO: Get with JOT and find out how to pull out MAC Address via NETCONF |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 71 | # TODO: May need to refine capabilities into current, advertised, and peer |
| 72 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 73 | self._ofp_capabilities = kwargs.pop('ofp_capabilities', OFPPF_100GB_FD | OFPPF_FIBER) |
| 74 | self._ofp_state = kwargs.pop('ofp_state', OFPPS_LIVE) |
| 75 | self._current_speed = kwargs.pop('current_speed', OFPPF_100GB_FD) |
| 76 | self._max_speed = kwargs.pop('max_speed', OFPPF_100GB_FD) |
| 77 | self._device_port_no = kwargs.pop('device_port_no', self._port_no) |
| 78 | |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 79 | # Statistics |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 80 | self.rx_dropped = 0 |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 81 | self.rx_error_packets = 0 |
| 82 | self.rx_ucast_packets = 0 |
| 83 | self.rx_bcast_packets = 0 |
| 84 | self.rx_mcast_packets = 0 |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 85 | self.tx_dropped = 0 |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 86 | self.rx_ucast_packets = 0 |
| 87 | self.tx_bcast_packets = 0 |
| 88 | self.tx_mcast_packets = 0 |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 89 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 90 | def __str__(self): |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 91 | return "NniPort-{}: Admin: {}, Oper: {}, parent: {}".format(self._port_no, |
| 92 | self._admin_state, |
| 93 | self._oper_status, |
| 94 | self._parent) |
| 95 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 96 | def get_port(self): |
| 97 | """ |
| 98 | Get the VOLTHA PORT object for this port |
| 99 | :return: VOLTHA Port object |
| 100 | """ |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 101 | self.log.debug('get-port-status-update', port=self._port_no, |
Chip Boling | bb15b51 | 2018-06-01 11:39:58 -0500 | [diff] [blame] | 102 | label=self._label) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 103 | if self._port is None: |
| 104 | self._port = Port(port_no=self._port_no, |
| 105 | label=self._label, |
| 106 | type=Port.ETHERNET_NNI, |
| 107 | admin_state=self._admin_state, |
| 108 | oper_status=self._oper_status) |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 109 | |
| 110 | if self._port.admin_state != self._admin_state or\ |
| 111 | self._port.oper_status != self._oper_status: |
| 112 | |
| 113 | self.log.debug('get-port-status-update', admin_state=self._admin_state, |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 114 | oper_status=self._oper_status) |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 115 | self._port.admin_state = self._admin_state |
| 116 | self._port.oper_status = self._oper_status |
| 117 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 118 | return self._port |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 119 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 120 | @property |
| 121 | def iana_type(self): |
| 122 | return self._ianatype |
| 123 | |
| 124 | def cancel_deferred(self): |
| 125 | super(NniPort, self).cancel_deferred() |
| 126 | |
| 127 | d, self._stats_deferred = self._stats_deferred, None |
| 128 | try: |
| 129 | if d is not None and d.called: |
| 130 | d.cancel() |
| 131 | except: |
| 132 | pass |
| 133 | |
| 134 | def _update_adapter_agent(self): |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 135 | # adapter_agent add_port also does an update of port status |
| 136 | self.log.debug('update-adapter-agent', admin_state=self._admin_state, |
Chip Boling | bb15b51 | 2018-06-01 11:39:58 -0500 | [diff] [blame] | 137 | oper_status=self._oper_status) |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 138 | self.adapter_agent.add_port(self.olt.device_id, self.get_port()) |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 139 | |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 140 | def get_logical_port(self): |
| 141 | """ |
| 142 | Get the VOLTHA logical port for this port |
| 143 | :return: VOLTHA logical port or None if not supported |
| 144 | """ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 145 | if self._logical_port is None: |
| 146 | openflow_port = ofp_port(port_no=self._port_no, |
| 147 | hw_addr=mac_str_to_tuple(self._mac_address), |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 148 | name=self._logical_port_name, |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 149 | config=0, |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 150 | state=self._ofp_state, |
| 151 | curr=self._ofp_capabilities, |
| 152 | advertised=self._ofp_capabilities, |
| 153 | peer=self._ofp_capabilities, |
| 154 | curr_speed=self._current_speed, |
| 155 | max_speed=self._max_speed) |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 156 | |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 157 | self._logical_port = LogicalPort(id=self._logical_port_name, |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 158 | ofp_port=openflow_port, |
| 159 | device_id=self._parent.device_id, |
| 160 | device_port_no=self._device_port_no, |
| 161 | root_port=True) |
| 162 | return self._logical_port |
Chip Boling | 3e3b1a9 | 2017-05-16 11:51:18 -0500 | [diff] [blame] | 163 | |
Chip Boling | 4864696 | 2017-08-20 09:41:18 -0500 | [diff] [blame] | 164 | @inlineCallbacks |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 165 | def finish_startup(self): |
| 166 | |
| 167 | if self.state != AdtnPort.State.INITIAL: |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 168 | returnValue('Done') |
| 169 | |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 170 | self.log.debug('final-startup') |
Chip Boling | ef0e2fa | 2017-10-06 14:33:01 -0500 | [diff] [blame] | 171 | # TODO: Start status polling of NNI interfaces |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 172 | self.deferred = None # = reactor.callLater(3, self.do_stuff) |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 173 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 174 | # Begin statistics sync |
| 175 | self._stats_deferred = reactor.callLater(self._stats_tick * 2, self._update_statistics) |
Chip Boling | ef0e2fa | 2017-10-06 14:33:01 -0500 | [diff] [blame] | 176 | |
Chip Boling | 4864696 | 2017-08-20 09:41:18 -0500 | [diff] [blame] | 177 | try: |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 178 | yield self.set_config('enabled', True) |
| 179 | |
| 180 | super(NniPort, self).finish_startup() |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 181 | |
Chip Boling | 4864696 | 2017-08-20 09:41:18 -0500 | [diff] [blame] | 182 | except Exception as e: |
| 183 | self.log.exception('nni-start', e=e) |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 184 | self._oper_status = OperStatus.UNKNOWN |
| 185 | self._update_adapter_agent() |
| 186 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 187 | returnValue('Enabled') |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 188 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 189 | def finish_stop(self): |
Chip Boling | 69fba86 | 2017-08-18 15:11:32 -0500 | [diff] [blame] | 190 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 191 | # NOTE: Leave all NNI ports active (may have inband management) |
| 192 | # TODO: Revisit leaving NNI Ports active on disable |
| 193 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 194 | return self.set_config('enabled', False) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 195 | |
| 196 | @inlineCallbacks |
| 197 | def reset(self): |
| 198 | """ |
| 199 | Set the NNI Port to a known good state on initial port startup. Actual |
| 200 | NNI 'Start' is done elsewhere |
| 201 | """ |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 202 | # if self.state != AdtnPort.State.INITIAL: |
| 203 | # self.log.error('reset-ignored', state=self.state) |
| 204 | # returnValue('Ignored') |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 205 | |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 206 | self.log.info('resetting', label=self._label) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 207 | |
| 208 | # Always enable our NNI ports |
| 209 | |
| 210 | try: |
| 211 | results = yield self.set_config('enabled', True) |
| 212 | self._admin_state = AdminState.ENABLED |
| 213 | self._enabled = True |
| 214 | returnValue(results) |
| 215 | |
| 216 | except Exception as e: |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 217 | self.log.exception('reset', e=e) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 218 | self._admin_state = AdminState.UNKNOWN |
| 219 | raise |
| 220 | |
| 221 | @inlineCallbacks |
| 222 | def set_config(self, leaf, value): |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 223 | if isinstance(value, bool): |
| 224 | value = 'true' if value else 'false' |
| 225 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 226 | config = '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">' + \ |
| 227 | ' <interface>' + \ |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 228 | ' <name>{}</name>'.format(self._physical_port_name) + \ |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 229 | ' {}'.format(self._ianatype) + \ |
| 230 | ' <{}>{}</{}>'.format(leaf, value, leaf) + \ |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 231 | ' </interface>' + \ |
| 232 | '</interfaces>' |
| 233 | try: |
| 234 | results = yield self._parent.netconf_client.edit_config(config) |
| 235 | returnValue(results) |
| 236 | |
| 237 | except Exception as e: |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 238 | self.log.exception('set', leaf=leaf, value=value, e=e) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 239 | raise |
| 240 | |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 241 | def get_nni_config(self): |
| 242 | config = '<filter xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">' + \ |
| 243 | ' <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">' + \ |
| 244 | ' <interface>' + \ |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 245 | ' <name>{}</name>'.format(self._physical_port_name) + \ |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 246 | ' <enabled/>' + \ |
| 247 | ' </interface>' + \ |
| 248 | ' </interfaces>' + \ |
| 249 | '</filter>' |
| 250 | return self._parent.netconf_client.get(config) |
| 251 | |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 252 | def get_nni_statistics(self): |
| 253 | state = '<filter xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">' + \ |
| 254 | ' <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">' + \ |
| 255 | ' <interface>' + \ |
Chip Boling | 3eedf46 | 2018-10-17 12:18:28 -0500 | [diff] [blame] | 256 | ' <name>{}</name>'.format(self._physical_port_name) + \ |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 257 | ' <admin-status/>' + \ |
| 258 | ' <oper-status/>' + \ |
| 259 | ' <statistics/>' + \ |
| 260 | ' </interface>' + \ |
| 261 | ' </interfaces>' + \ |
| 262 | '</filter>' |
| 263 | return self._parent.netconf_client.get(state) |
| 264 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 265 | def sync_hardware(self): |
| 266 | if self.state == AdtnPort.State.RUNNING or self.state == AdtnPort.State.STOPPED: |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 267 | def read_config(results): |
Chip Boling | bb15b51 | 2018-06-01 11:39:58 -0500 | [diff] [blame] | 268 | #self.log.debug('read-config', results=results) |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 269 | try: |
| 270 | result_dict = xmltodict.parse(results.data_xml) |
Chip Boling | c64c8dc | 2018-04-20 14:42:24 -0500 | [diff] [blame] | 271 | interfaces = result_dict['data']['interfaces'] |
| 272 | if 'if:interface' in interfaces: |
| 273 | entries = interfaces['if:interface'] |
| 274 | else: |
| 275 | entries = interfaces['interface'] |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 276 | |
| 277 | enabled = entries.get('enabled', |
| 278 | str(not self.enabled).lower()) == 'true' |
| 279 | |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 280 | if self.enabled == enabled: |
| 281 | return succeed('in-sync') |
| 282 | |
| 283 | self.set_config('enabled', self.enabled) |
| 284 | self._oper_status = OperStatus.ACTIVE |
| 285 | self._update_adapter_agent() |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 286 | |
| 287 | except Exception as e: |
| 288 | self.log.exception('read-config', e=e) |
| 289 | return fail(Failure()) |
| 290 | |
| 291 | def failure(reason): |
| 292 | self.log.error('hardware-sync-failed', reason=reason) |
| 293 | |
| 294 | def reschedule(_): |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 295 | delay = self.sync_tick |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 296 | delay += random.uniform(-delay / 10, delay / 10) |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 297 | self.sync_deferred = reactor.callLater(delay, self.sync_hardware) |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 298 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 299 | self.sync_deferred = self.get_nni_config() |
| 300 | self.sync_deferred.addCallbacks(read_config, failure) |
| 301 | self.sync_deferred.addBoth(reschedule) |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 302 | |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 303 | def _decode_nni_statistics(self, entry): |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 304 | # admin_status = entry.get('admin-status') |
| 305 | # oper_status = entry.get('oper-status') |
| 306 | # admin_status = entry.get('admin-status') |
| 307 | # phys_address = entry.get('phys-address') |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 308 | |
| 309 | stats = entry.get('statistics') |
| 310 | if stats is not None: |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 311 | self.timestamp = arrow.utcnow().float_timestamp |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 312 | self.rx_bytes = int(stats.get('in-octets', 0)) |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 313 | self.rx_ucast_packets = int(stats.get('in-unicast-pkts', 0)) |
| 314 | self.rx_bcast_packets = int(stats.get('in-broadcast-pkts', 0)) |
| 315 | self.rx_mcast_packets = int(stats.get('in-multicast-pkts', 0)) |
| 316 | self.rx_error_packets = int(stats.get('in-errors', 0)) + int(stats.get('in-discards', 0)) |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 317 | |
| 318 | self.tx_bytes = int(stats.get('out-octets', 0)) |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 319 | self.tx_ucast_packets = int(stats.get('out-unicast-pkts', 0)) |
| 320 | self.tx_bcast_packets = int(stats.get('out-broadcast-pkts', 0)) |
| 321 | self.tx_mcasy_packets = int(stats.get('out-multicast-pkts', 0)) |
| 322 | self.tx_error_packets = int(stats.get('out-errors', 0)) + int(stats.get('out-discards', 0)) |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 323 | |
Chip Boling | c045138 | 2018-08-24 14:21:53 -0500 | [diff] [blame] | 324 | self.rx_packets = self.rx_ucast_packets + self.rx_mcast_packets + self.rx_bcast_packets |
| 325 | self.tx_packets = self.tx_ucast_packets + self.tx_mcast_packets + self.tx_bcast_packets |
| 326 | # No support for rx_crc_errors or bip_errors |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 327 | |
| 328 | def _update_statistics(self): |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 329 | if self.state == AdtnPort.State.RUNNING: |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 330 | def read_state(results): |
Chip Boling | bb15b51 | 2018-06-01 11:39:58 -0500 | [diff] [blame] | 331 | # self.log.debug('read-state', results=results) |
Chip Boling | fd1fd37 | 2017-12-20 13:34:12 -0600 | [diff] [blame] | 332 | try: |
| 333 | result_dict = xmltodict.parse(results.data_xml) |
| 334 | entry = result_dict['data']['interfaces-state']['interface'] |
| 335 | self._decode_nni_statistics(entry) |
| 336 | return succeed('done') |
| 337 | |
| 338 | except Exception as e: |
| 339 | self.log.exception('read-state', e=e) |
| 340 | return fail(Failure()) |
| 341 | |
| 342 | def failure(reason): |
| 343 | self.log.error('update-stats-failed', reason=reason) |
| 344 | |
| 345 | def reschedule(_): |
| 346 | delay = self._stats_tick |
| 347 | delay += random.uniform(-delay / 10, delay / 10) |
| 348 | self._stats_deferred = reactor.callLater(delay, self._update_statistics) |
| 349 | |
| 350 | try: |
| 351 | self._stats_deferred = self.get_nni_statistics() |
| 352 | self._stats_deferred.addCallbacks(read_state, failure) |
| 353 | self._stats_deferred.addBoth(reschedule) |
| 354 | |
| 355 | except Exception as e: |
| 356 | self.log.exception('nni-sync', port=self.name, e=e) |
| 357 | self._stats_deferred = reactor.callLater(self._stats_tick, self._update_statistics) |
| 358 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 359 | |
| 360 | class MockNniPort(NniPort): |
| 361 | """ |
| 362 | A class similar to the 'Port' class in the VOLTHA but for a non-existent (virtual OLT) |
| 363 | |
| 364 | TODO: Merge this with the Port class or cleanup where possible |
| 365 | so we do not duplicate fields/properties/methods |
| 366 | """ |
| 367 | |
| 368 | def __init__(self, parent, **kwargs): |
| 369 | super(MockNniPort, self).__init__(parent, **kwargs) |
| 370 | |
| 371 | def __str__(self): |
| 372 | return "NniPort-mock-{}: Admin: {}, Oper: {}, parent: {}".format(self._port_no, |
| 373 | self._admin_state, |
| 374 | self._oper_status, |
| 375 | self._parent) |
| 376 | |
| 377 | @staticmethod |
| 378 | def get_nni_port_state_results(): |
| 379 | from ncclient.operations.retrieve import GetReply |
| 380 | raw = """ |
| 381 | <?xml version="1.0" encoding="UTF-8"?> |
| 382 | <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" |
| 383 | xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" |
| 384 | message-id="urn:uuid:59e71979-01bb-462f-b17a-b3a45e1889ac"> |
| 385 | <data> |
| 386 | <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> |
| 387 | <interface><name>hundred-gigabit-ethernet 0/1</name></interface> |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 388 | </interfaces-state> |
| 389 | </data> |
| 390 | </rpc-reply> |
| 391 | """ |
| 392 | return GetReply(raw) |
| 393 | |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 394 | @staticmethod |
| 395 | def get_pon_port_state_results(): |
| 396 | from ncclient.operations.retrieve import GetReply |
| 397 | raw = """ |
| 398 | <?xml version="1.0" encoding="UTF-8"?> |
| 399 | <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" |
| 400 | xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" |
| 401 | message-id="urn:uuid:59e71979-01bb-462f-b17a-b3a45e1889ac"> |
| 402 | <data> |
| 403 | <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> |
| 404 | <interface><name>XPON 0/1</name></interface> |
| 405 | <interface><name>XPON 0/2</name></interface> |
| 406 | <interface><name>XPON 0/3</name></interface> |
| 407 | <interface><name>XPON 0/4</name></interface> |
| 408 | <interface><name>XPON 0/5</name></interface> |
| 409 | <interface><name>XPON 0/6</name></interface> |
| 410 | <interface><name>XPON 0/7</name></interface> |
| 411 | <interface><name>XPON 0/8</name></interface> |
| 412 | <interface><name>XPON 0/9</name></interface> |
| 413 | <interface><name>XPON 0/10</name></interface> |
| 414 | <interface><name>XPON 0/11</name></interface> |
| 415 | <interface><name>XPON 0/12</name></interface> |
| 416 | <interface><name>XPON 0/13</name></interface> |
| 417 | <interface><name>XPON 0/14</name></interface> |
| 418 | <interface><name>XPON 0/15</name></interface> |
| 419 | <interface><name>XPON 0/16</name></interface> |
| 420 | </interfaces-state> |
| 421 | </data> |
| 422 | </rpc-reply> |
| 423 | """ |
| 424 | return GetReply(raw) |
| 425 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 426 | def reset(self): |
| 427 | """ |
| 428 | Set the NNI Port to a known good state on initial port startup. Actual |
| 429 | NNI 'Start' is done elsewhere |
| 430 | """ |
Chip Boling | ab8863d | 2018-03-22 14:50:31 -0500 | [diff] [blame] | 431 | if self.state != AdtnPort.State.INITIAL: |
| 432 | self.log.error('reset-ignored', state=self.state) |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 433 | return fail() |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 434 | |
Chip Boling | 2727599 | 2017-09-22 15:17:04 -0500 | [diff] [blame] | 435 | self.log.info('resetting', label=self._label) |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 436 | |
| 437 | # Always enable our NNI ports |
| 438 | |
| 439 | self._enabled = True |
| 440 | self._admin_state = AdminState.ENABLED |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 441 | return succeed('Enabled') |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 442 | |
Chip Boling | 7294b25 | 2017-06-15 16:16:55 -0500 | [diff] [blame] | 443 | def set_config(self, leaf, value): |
| 444 | |
| 445 | if leaf == 'enabled': |
| 446 | self._enabled = value |
| 447 | else: |
| 448 | raise NotImplemented("Leaf '{}' is not supported".format(leaf)) |
| 449 | |
Chip Boling | 5561d55 | 2017-07-07 15:11:26 -0500 | [diff] [blame] | 450 | return succeed('Success') |