blob: 5d8e9b789bb7d57e9df39f1078e8d94126390115 [file] [log] [blame]
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -05001#
2# Copyright 2018 the original author or authors.
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
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050016from __future__ import absolute_import
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050017import structlog
18from twisted.internet.defer import inlineCallbacks, returnValue
ozgecanetsiab7f73092020-02-16 23:36:25 +030019from pyvoltha.adapters.extensions.omci.omci_me import GemInterworkingTpFrame, GemPortNetworkCtpFrame, MulticastGemInterworkingTPFrame
20from pyvoltha.adapters.extensions.omci.omci_entities import IPv4MulticastAddressTable
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050021from pyvoltha.adapters.extensions.omci.omci_defs import ReasonCodes
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050022
23RC = ReasonCodes
24
25
26class OnuGemPort(object):
27 """
28 Broadcom ONU specific implementation
29 """
30
31 def __init__(self, gem_id, uni_id, alloc_id,
32 entity_id=None,
33 direction="BIDIRECTIONAL",
34 encryption=False,
35 discard_config=None,
36 discard_policy=None,
37 max_q_size="auto",
38 pbit_map="0b00000011",
39 priority_q=3,
40 scheduling_policy="WRR",
41 weight=8,
42 omci_transport=False,
43 multicast=False,
44 tcont_ref=None,
45 traffic_class=None,
46 intf_ref=None,
47 untagged=False,
48 name=None,
49 handler=None):
50
51 self.log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id, gem_id=gem_id)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050052
53 self.name = name
54 self.gem_id = gem_id
55 self.uni_id = uni_id
56 self._alloc_id = alloc_id
57 self.tcont_ref = tcont_ref
58 self.intf_ref = intf_ref
59 self.traffic_class = traffic_class
60 self._direction = None
61 self._encryption = encryption
62 self._discard_config = None
63 self._discard_policy = None
64 self._max_q_size = None
65 self._pbit_map = None
66 self._scheduling_policy = None
67 self._omci_transport = omci_transport
68 self.multicast = multicast
69 self.untagged = untagged
70 self._handler = handler
71
72 self.direction = direction
73 self.encryption = encryption
74 self.discard_config = discard_config
75 self.discard_policy = discard_policy
76 self.max_q_size = max_q_size
77 self.pbit_map = pbit_map
78 self.priority_q = priority_q
79 self.scheduling_policy = scheduling_policy
80 self.weight = weight
81
82 self._pon_id = None
83 self._onu_id = None
84 self._entity_id = entity_id
85
86 # Statistics
87 self.rx_packets = 0
88 self.rx_bytes = 0
89 self.tx_packets = 0
90 self.tx_bytes = 0
91
92 def __str__(self):
Mahir Gunyel5de33fe2020-03-03 22:38:44 -080093 return "OnuGemPort - entity_id {}, alloc-id: {}, gem-id: {}, direction: {}, multicast: {} ".format(self.entity_id, self.alloc_id,
94 self.gem_id, self.direction, self.multicast)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050095
96 def __repr__(self):
97 return str(self)
98
99 @property
Mahir Gunyel5de33fe2020-03-03 22:38:44 -0800100 def mcast(self):
101 return self.multicast
102
103 @property
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500104 def pon_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500105 return self._pon_id
106
107 @pon_id.setter
108 def pon_id(self, pon_id):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500109 assert self._pon_id is None or self._pon_id == pon_id, 'PON-ID can only be set once'
110 self._pon_id = pon_id
111
112 @property
113 def onu_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500114 return self._onu_id
115
116 @onu_id.setter
117 def onu_id(self, onu_id):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500118 assert self._onu_id is None or self._onu_id == onu_id, 'ONU-ID can only be set once'
119 self._onu_id = onu_id
120
121 @property
122 def alloc_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500123 return self._alloc_id
124
125 @property
126 def direction(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500127 return self._direction
128
129 @direction.setter
130 def direction(self, direction):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500131 # GEM Port CTP are configured separately in UPSTREAM and DOWNSTREAM.
132 # BIDIRECTIONAL is not supported.
133 assert direction == "UPSTREAM" or direction == "DOWNSTREAM" or \
134 direction == "BIDIRECTIONAL", "invalid-direction"
135
136 # OMCI framework expects string in lower-case. Tech-Profile sends in upper-case.
137 if direction == "UPSTREAM":
138 self._direction = "upstream"
139 elif direction == "DOWNSTREAM":
140 self._direction = "downstream"
141 elif direction == "BIDIRECTIONAL":
142 self._direction = "bi-directional"
143
144 @property
145 def tcont(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500146 tcont_item = self._handler.pon_port.tconts.get(self.alloc_id)
147 return tcont_item
148
149 @property
150 def omci_transport(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500151 return self._omci_transport
152
153 def to_dict(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500154 return {
155 'port-id': self.gem_id,
156 'alloc-id': self.alloc_id,
157 'encryption': self._encryption,
158 'omci-transport': self.omci_transport
159 }
160
161 @property
162 def entity_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500163 return self._entity_id
164
165 @entity_id.setter
166 def entity_id(self, value):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500167 self._entity_id = value
168
169 @property
170 def encryption(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500171 return self._encryption
172
173 @encryption.setter
174 def encryption(self, value):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500175 # FIXME The encryption should come as boolean by default
176 value = eval(value)
177 assert isinstance(value, bool), 'encryption is a boolean'
178
179 if self._encryption != value:
180 self._encryption = value
181
182 @property
183 def discard_config(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500184 return self._discard_config
185
186 @discard_config.setter
187 def discard_config(self, discard_config):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500188 assert isinstance(discard_config, dict), "discard_config not dict"
189 assert 'max_probability' in discard_config, "max_probability missing"
190 assert 'max_threshold' in discard_config, "max_threshold missing"
191 assert 'min_threshold' in discard_config, "min_threshold missing"
192 self._discard_config = discard_config
193
194 @property
195 def discard_policy(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500196 return self._discard_policy
197
198 @discard_policy.setter
199 def discard_policy(self, discard_policy):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500200 dp = ("TailDrop", "WTailDrop", "RED", "WRED")
201 assert (isinstance(discard_policy, str))
202 assert (discard_policy in dp)
203 self._discard_policy = discard_policy
204
205 @property
206 def max_q_size(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500207 return self._max_q_size
208
209 @max_q_size.setter
210 def max_q_size(self, max_q_size):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500211 if isinstance(max_q_size, str):
212 assert (max_q_size == "auto")
213 else:
214 assert (isinstance(max_q_size, int))
215
216 self._max_q_size = max_q_size
217
218 @property
219 def pbit_map(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500220 return self._pbit_map
221
222 @pbit_map.setter
223 def pbit_map(self, pbit_map):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500224 assert (isinstance(pbit_map, str))
225 assert (len(pbit_map[2:]) == 8) # Example format of pbit_map: "0b00000101"
226 try:
227 _ = int(pbit_map[2], 2)
228 except ValueError:
229 raise Exception("pbit_map-not-binary-string-{}".format(pbit_map))
230
231 # remove '0b'
232 self._pbit_map = pbit_map[2:]
233
234 @property
235 def scheduling_policy(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500236 return self._scheduling_policy
237
238 @scheduling_policy.setter
239 def scheduling_policy(self, scheduling_policy):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500240 sp = ("WRR", "StrictPriority")
241 assert (isinstance(scheduling_policy, str))
242 assert (scheduling_policy in sp)
243 self._scheduling_policy = scheduling_policy
244
245 @staticmethod
246 def create(handler, gem_port):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500247
248 return OnuGemPort(gem_id=gem_port['gemport_id'],
249 uni_id=gem_port['uni_id'],
250 alloc_id=gem_port['alloc_id_ref'],
251 direction=gem_port['direction'],
252 encryption=gem_port['encryption'], # aes_indicator,
253 discard_config=gem_port['discard_config'],
254 discard_policy=gem_port['discard_policy'],
255 max_q_size=gem_port['max_q_size'],
256 pbit_map=gem_port['pbit_map'],
257 priority_q=gem_port['priority_q'],
258 scheduling_policy=gem_port['scheduling_policy'],
259 weight=gem_port['weight'],
Mahir Gunyel5de33fe2020-03-03 22:38:44 -0800260 multicast=gem_port['is_multicast'],
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500261 handler=handler,
262 untagged=False)
263
264 @inlineCallbacks
265 def add_to_hardware(self, omci,
266 tcont_entity_id,
267 ieee_mapper_service_profile_entity_id,
268 gal_enet_profile_entity_id,
269 ul_prior_q_entity_id,
270 dl_prior_q_entity_id):
271
272 self.log.debug('add-to-hardware', entity_id=self.entity_id, gem_id=self.gem_id,
273 tcont_entity_id=tcont_entity_id,
274 ieee_mapper_service_profile_entity_id=ieee_mapper_service_profile_entity_id,
275 gal_enet_profile_entity_id=gal_enet_profile_entity_id,
276 ul_prior_q_entity_id=ul_prior_q_entity_id,
Mahir Gunyel5de33fe2020-03-03 22:38:44 -0800277 dl_prior_q_entity_id=dl_prior_q_entity_id,
278 multicast=self.multicast)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500279
280 try:
281 direction = "downstream" if self.multicast else "bi-directional"
ozgecanetsiab7f73092020-02-16 23:36:25 +0300282 entity_id = self.gem_id if self.multicast else self.entity_id
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500283
284 attributes = dict()
285 attributes['priority_queue_pointer_downstream'] = dl_prior_q_entity_id
286 msg = GemPortNetworkCtpFrame(
Mahir Gunyel5de33fe2020-03-03 22:38:44 -0800287 entity_id, # same entity id as GEM port
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500288 port_id=self.gem_id,
289 tcont_id=tcont_entity_id,
290 direction=direction,
291 upstream_tm=ul_prior_q_entity_id,
292 attributes=attributes
293 )
294 frame = msg.create()
295 self.log.debug('openomci-msg', omci_msg=msg)
296 results = yield omci.send(frame)
297 self.check_status_and_state(results, 'create-gem-port-network-ctp')
298
299 except Exception as e:
300 self.log.exception('gemport-create', e=e)
301 raise
302
303 try:
ozgecanetsiab7f73092020-02-16 23:36:25 +0300304 if self.multicast:
305 # 224.0.0.0 - 239.255.255.255 is valid multicast ip range
306 mcast_ip_list= IPv4MulticastAddressTable( gem_port_id=self.gem_id,
307 secondary_key=0,
308 multicast_ip_range_start="224.0.0.0",
309 multicast_ip_range_stop="239.255.255.255")
310 msg = MulticastGemInterworkingTPFrame(
311 self.gem_id,
312 gem_port_network_ctp_pointer=self.gem_id,
313 interworking_option=0, # Mac Bridge
314 service_profile_pointer=0,
315 gal_profile_pointer=gal_enet_profile_entity_id,
316 ipv4_multicast_address_table= mcast_ip_list
317 )
318 else:
319 msg = GemInterworkingTpFrame(
320 self.entity_id, # same entity id as GEM port
321 gem_port_network_ctp_pointer=self.entity_id,
322 interworking_option=5, # IEEE 802.1
323 service_profile_pointer=ieee_mapper_service_profile_entity_id,
324 interworking_tp_pointer=0x0,
325 pptp_counter=1,
326 gal_profile_pointer=gal_enet_profile_entity_id
327 )
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500328 frame = msg.create()
329 self.log.debug('openomci-msg', omci_msg=msg)
330 results = yield omci.send(frame)
331 self.check_status_and_state(results, 'create-gem-interworking-tp')
332
333 except Exception as e:
334 self.log.exception('interworking-create', e=e)
335 raise
336
337 returnValue(results)
338
339 @inlineCallbacks
340 def remove_from_hardware(self, omci):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500341 self.log.debug('remove-from-hardware', gem_id=self.gem_id)
342
343 try:
344 msg = GemInterworkingTpFrame(self.entity_id)
345 frame = msg.delete()
346 self.log.debug('openomci-msg', omci_msg=msg)
347 results = yield omci.send(frame)
348 self.check_status_and_state(results, 'delete-gem-port-network-ctp')
349 except Exception as e:
350 self.log.exception('interworking-delete', e=e)
351 raise
352
353 try:
354 msg = GemPortNetworkCtpFrame(self.entity_id)
355 frame = msg.delete()
356 self.log.debug('openomci-msg', omci_msg=msg)
357 results = yield omci.send(frame)
358 self.check_status_and_state(results, 'delete-gem-interworking-tp')
359 except Exception as e:
360 self.log.exception('gemport-delete', e=e)
361 raise
362
363 returnValue(results)
364
365 def check_status_and_state(self, results, operation=''):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500366 omci_msg = results.fields['omci_message'].fields
367 status = omci_msg['success_code']
368 error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
369 failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
370 unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
371
Matt Jeannerete8fc53e2019-04-13 15:58:33 -0400372 self.log.debug("OMCI Result", operation=operation, omci_msg=omci_msg,
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500373 status=status, error_mask=error_mask,
374 failed_mask=failed_mask, unsupported_mask=unsupported_mask)
375
376 if status == RC.Success:
377 return True
378
379 elif status == RC.InstanceExists:
380 return False