blob: 1ee28a641e7388fbaa14b444efeeefdc294a0667 [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
16import structlog
17from twisted.internet.defer import inlineCallbacks, returnValue
Matt Jeanneret72f96fc2019-02-11 10:53:05 -050018from pyvoltha.adapters.extensions.omci.omci_me import *
19from pyvoltha.adapters.extensions.omci.omci_defs import *
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050020
21RC = ReasonCodes
22
23
24class OnuGemPort(object):
25 """
26 Broadcom ONU specific implementation
27 """
28
29 def __init__(self, gem_id, uni_id, alloc_id,
30 entity_id=None,
31 direction="BIDIRECTIONAL",
32 encryption=False,
33 discard_config=None,
34 discard_policy=None,
35 max_q_size="auto",
36 pbit_map="0b00000011",
37 priority_q=3,
38 scheduling_policy="WRR",
39 weight=8,
40 omci_transport=False,
41 multicast=False,
42 tcont_ref=None,
43 traffic_class=None,
44 intf_ref=None,
45 untagged=False,
46 name=None,
47 handler=None):
48
49 self.log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id, gem_id=gem_id)
50 self.log.debug('function-entry')
51
52 self.name = name
53 self.gem_id = gem_id
54 self.uni_id = uni_id
55 self._alloc_id = alloc_id
56 self.tcont_ref = tcont_ref
57 self.intf_ref = intf_ref
58 self.traffic_class = traffic_class
59 self._direction = None
60 self._encryption = encryption
61 self._discard_config = None
62 self._discard_policy = None
63 self._max_q_size = None
64 self._pbit_map = None
65 self._scheduling_policy = None
66 self._omci_transport = omci_transport
67 self.multicast = multicast
68 self.untagged = untagged
69 self._handler = handler
70
71 self.direction = direction
72 self.encryption = encryption
73 self.discard_config = discard_config
74 self.discard_policy = discard_policy
75 self.max_q_size = max_q_size
76 self.pbit_map = pbit_map
77 self.priority_q = priority_q
78 self.scheduling_policy = scheduling_policy
79 self.weight = weight
80
81 self._pon_id = None
82 self._onu_id = None
83 self._entity_id = entity_id
84
85 # Statistics
86 self.rx_packets = 0
87 self.rx_bytes = 0
88 self.tx_packets = 0
89 self.tx_bytes = 0
90
91 def __str__(self):
92 return "OnuGemPort - entity_id {}, alloc-id: {}, gem-id: {}, ".format(self.entity_id, self.alloc_id, self.gem_id)
93
94 def __repr__(self):
95 return str(self)
96
97 @property
98 def pon_id(self):
99 self.log.debug('function-entry')
100 return self._pon_id
101
102 @pon_id.setter
103 def pon_id(self, pon_id):
104 self.log.debug('function-entry')
105 assert self._pon_id is None or self._pon_id == pon_id, 'PON-ID can only be set once'
106 self._pon_id = pon_id
107
108 @property
109 def onu_id(self):
110 self.log.debug('function-entry')
111 return self._onu_id
112
113 @onu_id.setter
114 def onu_id(self, onu_id):
115 self.log.debug('function-entry', onu_id=onu_id)
116 assert self._onu_id is None or self._onu_id == onu_id, 'ONU-ID can only be set once'
117 self._onu_id = onu_id
118
119 @property
120 def alloc_id(self):
121 self.log.debug('function-entry')
122 return self._alloc_id
123
124 @property
125 def direction(self):
126 self.log.debug('function-entry')
127 return self._direction
128
129 @direction.setter
130 def direction(self, direction):
131 self.log.debug('function-entry')
132 # GEM Port CTP are configured separately in UPSTREAM and DOWNSTREAM.
133 # BIDIRECTIONAL is not supported.
134 assert direction == "UPSTREAM" or direction == "DOWNSTREAM" or \
135 direction == "BIDIRECTIONAL", "invalid-direction"
136
137 # OMCI framework expects string in lower-case. Tech-Profile sends in upper-case.
138 if direction == "UPSTREAM":
139 self._direction = "upstream"
140 elif direction == "DOWNSTREAM":
141 self._direction = "downstream"
142 elif direction == "BIDIRECTIONAL":
143 self._direction = "bi-directional"
144
145 @property
146 def tcont(self):
147 self.log.debug('function-entry')
148 tcont_item = self._handler.pon_port.tconts.get(self.alloc_id)
149 return tcont_item
150
151 @property
152 def omci_transport(self):
153 self.log.debug('function-entry')
154 return self._omci_transport
155
156 def to_dict(self):
157 self.log.debug('function-entry')
158 return {
159 'port-id': self.gem_id,
160 'alloc-id': self.alloc_id,
161 'encryption': self._encryption,
162 'omci-transport': self.omci_transport
163 }
164
165 @property
166 def entity_id(self):
167 self.log.debug('function-entry')
168 return self._entity_id
169
170 @entity_id.setter
171 def entity_id(self, value):
172 self.log.debug('function-entry')
173 self._entity_id = value
174
175 @property
176 def encryption(self):
177 self.log.debug('function-entry')
178 return self._encryption
179
180 @encryption.setter
181 def encryption(self, value):
182 self.log.debug('function-entry')
183 # FIXME The encryption should come as boolean by default
184 value = eval(value)
185 assert isinstance(value, bool), 'encryption is a boolean'
186
187 if self._encryption != value:
188 self._encryption = value
189
190 @property
191 def discard_config(self):
192 self.log.debug('function-entry')
193 return self._discard_config
194
195 @discard_config.setter
196 def discard_config(self, discard_config):
197 self.log.debug('function-entry')
198 assert isinstance(discard_config, dict), "discard_config not dict"
199 assert 'max_probability' in discard_config, "max_probability missing"
200 assert 'max_threshold' in discard_config, "max_threshold missing"
201 assert 'min_threshold' in discard_config, "min_threshold missing"
202 self._discard_config = discard_config
203
204 @property
205 def discard_policy(self):
206 self.log.debug('function-entry')
207 return self._discard_policy
208
209 @discard_policy.setter
210 def discard_policy(self, discard_policy):
211 self.log.debug('function-entry')
212 dp = ("TailDrop", "WTailDrop", "RED", "WRED")
213 assert (isinstance(discard_policy, str))
214 assert (discard_policy in dp)
215 self._discard_policy = discard_policy
216
217 @property
218 def max_q_size(self):
219 self.log.debug('function-entry')
220 return self._max_q_size
221
222 @max_q_size.setter
223 def max_q_size(self, max_q_size):
224 self.log.debug('function-entry')
225 if isinstance(max_q_size, str):
226 assert (max_q_size == "auto")
227 else:
228 assert (isinstance(max_q_size, int))
229
230 self._max_q_size = max_q_size
231
232 @property
233 def pbit_map(self):
234 self.log.debug('function-entry')
235 return self._pbit_map
236
237 @pbit_map.setter
238 def pbit_map(self, pbit_map):
239 self.log.debug('function-entry')
240 assert (isinstance(pbit_map, str))
241 assert (len(pbit_map[2:]) == 8) # Example format of pbit_map: "0b00000101"
242 try:
243 _ = int(pbit_map[2], 2)
244 except ValueError:
245 raise Exception("pbit_map-not-binary-string-{}".format(pbit_map))
246
247 # remove '0b'
248 self._pbit_map = pbit_map[2:]
249
250 @property
251 def scheduling_policy(self):
252 self.log.debug('function-entry')
253 return self._scheduling_policy
254
255 @scheduling_policy.setter
256 def scheduling_policy(self, scheduling_policy):
257 self.log.debug('function-entry')
258 sp = ("WRR", "StrictPriority")
259 assert (isinstance(scheduling_policy, str))
260 assert (scheduling_policy in sp)
261 self._scheduling_policy = scheduling_policy
262
263 @staticmethod
264 def create(handler, gem_port):
265 log.debug('function-entry', gem_port=gem_port)
266
267 return OnuGemPort(gem_id=gem_port['gemport_id'],
268 uni_id=gem_port['uni_id'],
269 alloc_id=gem_port['alloc_id_ref'],
270 direction=gem_port['direction'],
271 encryption=gem_port['encryption'], # aes_indicator,
272 discard_config=gem_port['discard_config'],
273 discard_policy=gem_port['discard_policy'],
274 max_q_size=gem_port['max_q_size'],
275 pbit_map=gem_port['pbit_map'],
276 priority_q=gem_port['priority_q'],
277 scheduling_policy=gem_port['scheduling_policy'],
278 weight=gem_port['weight'],
279 handler=handler,
280 untagged=False)
281
282 @inlineCallbacks
283 def add_to_hardware(self, omci,
284 tcont_entity_id,
285 ieee_mapper_service_profile_entity_id,
286 gal_enet_profile_entity_id,
287 ul_prior_q_entity_id,
288 dl_prior_q_entity_id):
289
290 self.log.debug('add-to-hardware', entity_id=self.entity_id, gem_id=self.gem_id,
291 tcont_entity_id=tcont_entity_id,
292 ieee_mapper_service_profile_entity_id=ieee_mapper_service_profile_entity_id,
293 gal_enet_profile_entity_id=gal_enet_profile_entity_id,
294 ul_prior_q_entity_id=ul_prior_q_entity_id,
295 dl_prior_q_entity_id=dl_prior_q_entity_id)
296
297 try:
298 direction = "downstream" if self.multicast else "bi-directional"
299 assert not self.multicast, 'MCAST is not supported yet'
300
301 attributes = dict()
302 attributes['priority_queue_pointer_downstream'] = dl_prior_q_entity_id
303 msg = GemPortNetworkCtpFrame(
304 self.entity_id, # same entity id as GEM port
305 port_id=self.gem_id,
306 tcont_id=tcont_entity_id,
307 direction=direction,
308 upstream_tm=ul_prior_q_entity_id,
309 attributes=attributes
310 )
311 frame = msg.create()
312 self.log.debug('openomci-msg', omci_msg=msg)
313 results = yield omci.send(frame)
314 self.check_status_and_state(results, 'create-gem-port-network-ctp')
315
316 except Exception as e:
317 self.log.exception('gemport-create', e=e)
318 raise
319
320 try:
321 # TODO: magic numbers here
322 msg = GemInterworkingTpFrame(
323 self.entity_id, # same entity id as GEM port
324 gem_port_network_ctp_pointer=self.entity_id,
325 interworking_option=5, # IEEE 802.1
326 service_profile_pointer=ieee_mapper_service_profile_entity_id,
327 interworking_tp_pointer=0x0,
328 pptp_counter=1,
329 gal_profile_pointer=gal_enet_profile_entity_id
330 )
331 frame = msg.create()
332 self.log.debug('openomci-msg', omci_msg=msg)
333 results = yield omci.send(frame)
334 self.check_status_and_state(results, 'create-gem-interworking-tp')
335
336 except Exception as e:
337 self.log.exception('interworking-create', e=e)
338 raise
339
340 returnValue(results)
341
342 @inlineCallbacks
343 def remove_from_hardware(self, omci):
344 self.log.debug('function-entry', omci=omci)
345 self.log.debug('remove-from-hardware', gem_id=self.gem_id)
346
347 try:
348 msg = GemInterworkingTpFrame(self.entity_id)
349 frame = msg.delete()
350 self.log.debug('openomci-msg', omci_msg=msg)
351 results = yield omci.send(frame)
352 self.check_status_and_state(results, 'delete-gem-port-network-ctp')
353 except Exception as e:
354 self.log.exception('interworking-delete', e=e)
355 raise
356
357 try:
358 msg = GemPortNetworkCtpFrame(self.entity_id)
359 frame = msg.delete()
360 self.log.debug('openomci-msg', omci_msg=msg)
361 results = yield omci.send(frame)
362 self.check_status_and_state(results, 'delete-gem-interworking-tp')
363 except Exception as e:
364 self.log.exception('gemport-delete', e=e)
365 raise
366
367 returnValue(results)
368
369 def check_status_and_state(self, results, operation=''):
370 self.log.debug('function-entry')
371 omci_msg = results.fields['omci_message'].fields
372 status = omci_msg['success_code']
373 error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
374 failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
375 unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
376
Matt Jeannerete8fc53e2019-04-13 15:58:33 -0400377 self.log.debug("OMCI Result", operation=operation, omci_msg=omci_msg,
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500378 status=status, error_mask=error_mask,
379 failed_mask=failed_mask, unsupported_mask=unsupported_mask)
380
381 if status == RC.Success:
382 return True
383
384 elif status == RC.InstanceExists:
385 return False