Chip Boling | 8e042f6 | 2019-02-12 16:14:34 -0600 | [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 | import structlog |
| 17 | from adapters.adtran_common.xpon.gem_port import GemPort |
| 18 | from twisted.internet.defer import inlineCallbacks, returnValue |
| 19 | from pyvoltha.adapters.extensions.omci.omci_me import GemPortNetworkCtpFrame, GemInterworkingTpFrame |
| 20 | from pyvoltha.adapters.extensions.omci.omci_defs import ReasonCodes |
| 21 | |
| 22 | |
| 23 | class OnuGemPort(GemPort): |
| 24 | """ |
| 25 | Adtran ONU specific implementation |
| 26 | """ |
| 27 | UPSTREAM = 1 |
| 28 | DOWNSTREAM = 2 |
| 29 | BIDIRECTIONAL = 3 |
| 30 | |
| 31 | def __init__(self, handler, gem_data, alloc_id, tech_profile_id, |
| 32 | uni_id, entity_id, |
| 33 | multicast=False, traffic_class=None, is_mock=False): |
| 34 | gem_id = gem_data['gemport-id'] |
| 35 | self.log = structlog.get_logger(device_id=handler.device_id, gem_id=gem_id) |
| 36 | encryption = gem_data['encryption'] |
| 37 | |
| 38 | super(OnuGemPort, self).__init__(gem_id, alloc_id, uni_id, |
| 39 | tech_profile_id, |
| 40 | encryption=encryption, |
| 41 | multicast=multicast, |
| 42 | traffic_class=traffic_class, |
| 43 | handler=handler, |
| 44 | is_mock=is_mock) |
| 45 | try: |
| 46 | self._gem_data = gem_data |
| 47 | self._entity_id = entity_id |
| 48 | self._tcont_entity_id = None |
| 49 | self._interworking = False |
| 50 | self.uni_id = gem_data['uni-id'] |
| 51 | self.direction = gem_data.get('direction', OnuGemPort.BIDIRECTIONAL) |
| 52 | |
| 53 | # IEEE 802.1p Mapper ME (#130) related parameters |
| 54 | self._pbit_map = None |
| 55 | self.pbit_map = gem_data.get('pbit-map', '0b00000011') |
| 56 | |
| 57 | # Upstream priority queue ME (#277) related parameters |
| 58 | self.priority_q = gem_data.get('priority-q', 3) |
| 59 | |
| 60 | self._max_q_size = None |
| 61 | self.max_q_size = gem_data.get('max-q-size', 'auto') |
| 62 | |
| 63 | self.weight = gem_data.get('weight', 8) |
| 64 | |
| 65 | self._discard_config = None |
| 66 | self.discard_config = gem_data.get('discard-config', None) |
| 67 | |
| 68 | self._discard_policy = None |
| 69 | self.discard_policy = gem_data.get('discard-policy', 'TailDrop') |
| 70 | |
| 71 | # Traffic scheduler ME (#278) related parameters |
| 72 | self._scheduling_policy = None |
| 73 | self.scheduling_policy = gem_data.get('scheduling-policy', 'WRR') |
| 74 | |
| 75 | except Exception as _e: |
| 76 | raise |
| 77 | |
| 78 | @property |
| 79 | def entity_id(self): |
| 80 | return self._entity_id |
| 81 | |
| 82 | @property |
| 83 | def discard_config(self): |
| 84 | return self._discard_config |
| 85 | |
| 86 | @discard_config.setter |
| 87 | def discard_config(self, discard_config): |
| 88 | assert isinstance(discard_config, dict), "discard-config not dict" |
| 89 | assert 'max-probability' in discard_config, "max-probability missing" |
| 90 | assert 'max-threshold' in discard_config, "max-hreshold missing" |
| 91 | assert 'min-threshold' in discard_config, "min-threshold missing" |
| 92 | self._discard_config = discard_config |
| 93 | |
| 94 | @property |
| 95 | def discard_policy(self): |
| 96 | self.log.debug('function-entry') |
| 97 | return self._discard_policy |
| 98 | |
| 99 | @discard_policy.setter |
| 100 | def discard_policy(self, discard_policy): |
| 101 | dp = ("TailDrop", "WTailDrop", "RED", "WRED") |
| 102 | assert (isinstance(discard_policy, str)) |
| 103 | assert (discard_policy in dp) |
| 104 | self._discard_policy = discard_policy |
| 105 | |
| 106 | @property |
| 107 | def max_q_size(self): |
| 108 | return self._max_q_size |
| 109 | |
| 110 | @max_q_size.setter |
| 111 | def max_q_size(self, max_q_size): |
| 112 | if isinstance(max_q_size, str): |
| 113 | assert (max_q_size == "auto") |
| 114 | else: |
| 115 | assert (isinstance(max_q_size, int)) |
| 116 | |
| 117 | self._max_q_size = max_q_size |
| 118 | |
| 119 | @property |
| 120 | def pbit_map(self): |
| 121 | return self._pbit_map |
| 122 | |
| 123 | @pbit_map.setter |
| 124 | def pbit_map(self, pbit_map): |
| 125 | assert (isinstance(pbit_map, str)) |
| 126 | assert (len(pbit_map[2:]) == 8) # Example format of pbit_map: "0b00000101" |
| 127 | try: |
| 128 | _ = int(pbit_map[2], 2) |
| 129 | except ValueError: |
| 130 | raise Exception("pbit_map-not-binary-string-{}".format(pbit_map)) |
| 131 | |
| 132 | # remove '0b' |
| 133 | self._pbit_map = pbit_map[2:] |
| 134 | |
| 135 | @property |
| 136 | def scheduling_policy(self): |
| 137 | return self._scheduling_policy |
| 138 | |
| 139 | @scheduling_policy.setter |
| 140 | def scheduling_policy(self, scheduling_policy): |
| 141 | sp = ("WRR", "StrictPriority") |
| 142 | assert (isinstance(scheduling_policy, str)) |
| 143 | assert (scheduling_policy in sp) |
| 144 | self._scheduling_policy = scheduling_policy |
| 145 | |
| 146 | @property |
| 147 | def in_hardware(self): |
| 148 | return self._tcont_entity_id is not None and self._interworking |
| 149 | |
| 150 | @staticmethod |
| 151 | def create(handler, gem_data, alloc_id, tech_profile_id, uni_id, entity_id): |
| 152 | return OnuGemPort(handler, gem_data, alloc_id, |
| 153 | tech_profile_id, uni_id, entity_id) |
| 154 | |
| 155 | @property |
| 156 | def tcont(self): |
| 157 | """ Get the associated TCONT object """ |
| 158 | return self._handler.pon_port.tconts.get(self.alloc_id) |
| 159 | |
| 160 | @inlineCallbacks |
| 161 | def add_to_hardware(self, omci, |
| 162 | tcont_entity_id, |
| 163 | ieee_mapper_service_profile_entity_id, |
| 164 | gal_enet_profile_entity_id): |
| 165 | if self._is_mock: |
| 166 | returnValue('mock') |
| 167 | |
| 168 | self.log.debug('add-to-hardware', gem_id=self.gem_id, |
| 169 | gem_entity_id=self.entity_id, |
| 170 | tcont_entity_id=tcont_entity_id, |
| 171 | ieee_mapper_service_profile_entity_id=ieee_mapper_service_profile_entity_id, |
| 172 | gal_enet_profile_entity_id=gal_enet_profile_entity_id) |
| 173 | |
| 174 | if self._tcont_entity_id is not None and self._tcont_entity_id != tcont_entity_id: |
| 175 | raise KeyError('GEM Port already assigned to TCONT: {}'.format(self._tcont_entity_id)) |
| 176 | |
| 177 | results = None |
| 178 | if self._tcont_entity_id is None: |
| 179 | try: |
| 180 | direction = "downstream" if self.multicast else "bi-directional" |
| 181 | assert not self.multicast, 'MCAST is not supported yet' |
| 182 | |
| 183 | frame = GemPortNetworkCtpFrame( |
| 184 | self.entity_id, # same entity id as GEM port |
| 185 | port_id=self.gem_id, |
| 186 | tcont_id=tcont_entity_id, |
| 187 | direction=direction, |
| 188 | upstream_tm=0x8000 # TM ID, 32768 unique ID set in TD set TODO: Parameterize |
| 189 | # This is Priority Queue ME with this entity ID |
| 190 | # and the ME's related port value is 0x01.00.0007 |
| 191 | # which is slot=0x01, tcont# = 0x00, priority= 0x0007 |
| 192 | ).create() |
| 193 | results = yield omci.send(frame) |
| 194 | |
| 195 | status = results.fields['omci_message'].fields['success_code'] |
| 196 | error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask'] |
| 197 | self.log.debug('create-gem-port-network-ctp', status=status, error_mask=error_mask) |
| 198 | |
| 199 | if status == ReasonCodes.Success or status == ReasonCodes.InstanceExists: |
| 200 | self._tcont_entity_id = tcont_entity_id |
| 201 | else: |
| 202 | raise Exception('GEM Port create failed with status: {}'.format(status)) |
| 203 | |
| 204 | except Exception as e: |
| 205 | self.log.exception('gemport-create', e=e) |
| 206 | raise |
| 207 | |
| 208 | if not self._interworking: |
| 209 | try: |
| 210 | extra = {'gal_loopback_configuration': 0} # No loopback |
| 211 | |
| 212 | frame = GemInterworkingTpFrame( |
| 213 | self.entity_id, # same entity id as GEM port |
| 214 | gem_port_network_ctp_pointer=self.entity_id, |
| 215 | interworking_option=5, # IEEE 802.1 |
| 216 | service_profile_pointer=ieee_mapper_service_profile_entity_id, |
| 217 | interworking_tp_pointer=0x0, |
| 218 | pptp_counter=1, |
| 219 | gal_profile_pointer=gal_enet_profile_entity_id, |
| 220 | attributes=extra |
| 221 | ).create() |
| 222 | results = yield omci.send(frame) |
| 223 | |
| 224 | status = results.fields['omci_message'].fields['success_code'] |
| 225 | error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask'] |
| 226 | self.log.debug('create-gem-interworking-tp', status=status, error_mask=error_mask) |
| 227 | |
| 228 | if status == ReasonCodes.Success or status == ReasonCodes.InstanceExists: |
| 229 | self._interworking = True |
| 230 | else: |
| 231 | raise Exception('GEM Interworking create failed with status: {}'.format(status)) |
| 232 | |
| 233 | except Exception as e: |
| 234 | self.log.exception('interworking-create', e=e) |
| 235 | raise |
| 236 | |
| 237 | returnValue(results) |
| 238 | |
| 239 | @inlineCallbacks |
| 240 | def remove_from_hardware(self, omci): |
| 241 | if self._is_mock: |
| 242 | returnValue('mock') |
| 243 | |
| 244 | self.log.debug('remove-from-hardware', gem_id=self.gem_id) |
| 245 | |
| 246 | results = None |
| 247 | if self._interworking: |
| 248 | try: |
| 249 | frame = GemInterworkingTpFrame(self.entity_id).delete() |
| 250 | results = yield omci.send(frame) |
| 251 | status = results.fields['omci_message'].fields['success_code'] |
| 252 | self.log.debug('delete-gem-interworking-tp', status=status) |
| 253 | |
| 254 | if status == ReasonCodes.Success: |
| 255 | self._interworking = False |
| 256 | |
| 257 | except Exception as e: |
| 258 | self.log.exception('interworking-delete', e=e) |
| 259 | raise |
| 260 | |
| 261 | if self._tcont_entity_id is not None: |
| 262 | try: |
| 263 | frame = GemPortNetworkCtpFrame(self.entity_id).delete() |
| 264 | results = yield omci.send(frame) |
| 265 | |
| 266 | status = results.fields['omci_message'].fields['success_code'] |
| 267 | self.log.debug('delete-gem-port-network-ctp', status=status) |
| 268 | |
| 269 | if status == ReasonCodes.Success: |
| 270 | self._tcont_entity_id = None |
| 271 | |
| 272 | except Exception as e: |
| 273 | self.log.exception('gemport-delete', e=e) |
| 274 | raise |
| 275 | |
| 276 | returnValue(results) |