blob: de8ba04e03429b51567e1a1563c28217854f8fd7 [file] [log] [blame]
Chip Boling8e042f62019-02-12 16:14:34 -06001#
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
16import structlog
17from adapters.adtran_common.xpon.gem_port import GemPort
18from twisted.internet.defer import inlineCallbacks, returnValue
19from pyvoltha.adapters.extensions.omci.omci_me import GemPortNetworkCtpFrame, GemInterworkingTpFrame
20from pyvoltha.adapters.extensions.omci.omci_defs import ReasonCodes
21
22
23class 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)