blob: 6b00c2e537979673c5680e4a260431e99b7afe71 [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
Matt Jeanneret2e3cb8d2019-11-16 09:22:41 -050019from pyvoltha.adapters.extensions.omci.omci_me import GemInterworkingTpFrame, GemPortNetworkCtpFrame
20from pyvoltha.adapters.extensions.omci.omci_defs import ReasonCodes
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050021
22RC = ReasonCodes
23
24
25class OnuGemPort(object):
26 """
27 Broadcom ONU specific implementation
28 """
29
30 def __init__(self, gem_id, uni_id, alloc_id,
31 entity_id=None,
32 direction="BIDIRECTIONAL",
33 encryption=False,
34 discard_config=None,
35 discard_policy=None,
36 max_q_size="auto",
37 pbit_map="0b00000011",
38 priority_q=3,
39 scheduling_policy="WRR",
40 weight=8,
41 omci_transport=False,
42 multicast=False,
43 tcont_ref=None,
44 traffic_class=None,
45 intf_ref=None,
46 untagged=False,
47 name=None,
48 handler=None):
49
50 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 -050051
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):
Matt Jeanneret3789d0d2020-01-19 09:03:42 -050092 return "OnuGemPort - entity_id {}, alloc-id: {}, gem-id: {}".format(self.entity_id, self.alloc_id,
Girish Gowdrae933cd32019-11-21 21:04:41 +053093 self.gem_id)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050094
95 def __repr__(self):
96 return str(self)
97
98 @property
99 def pon_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500100 return self._pon_id
101
102 @pon_id.setter
103 def pon_id(self, pon_id):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500104 assert self._pon_id is None or self._pon_id == pon_id, 'PON-ID can only be set once'
105 self._pon_id = pon_id
106
107 @property
108 def onu_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500109 return self._onu_id
110
111 @onu_id.setter
112 def onu_id(self, onu_id):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500113 assert self._onu_id is None or self._onu_id == onu_id, 'ONU-ID can only be set once'
114 self._onu_id = onu_id
115
116 @property
117 def alloc_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500118 return self._alloc_id
119
120 @property
121 def direction(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500122 return self._direction
123
124 @direction.setter
125 def direction(self, direction):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500126 # GEM Port CTP are configured separately in UPSTREAM and DOWNSTREAM.
127 # BIDIRECTIONAL is not supported.
128 assert direction == "UPSTREAM" or direction == "DOWNSTREAM" or \
129 direction == "BIDIRECTIONAL", "invalid-direction"
130
131 # OMCI framework expects string in lower-case. Tech-Profile sends in upper-case.
132 if direction == "UPSTREAM":
133 self._direction = "upstream"
134 elif direction == "DOWNSTREAM":
135 self._direction = "downstream"
136 elif direction == "BIDIRECTIONAL":
137 self._direction = "bi-directional"
138
139 @property
140 def tcont(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500141 tcont_item = self._handler.pon_port.tconts.get(self.alloc_id)
142 return tcont_item
143
144 @property
145 def omci_transport(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500146 return self._omci_transport
147
148 def to_dict(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500149 return {
150 'port-id': self.gem_id,
151 'alloc-id': self.alloc_id,
152 'encryption': self._encryption,
153 'omci-transport': self.omci_transport
154 }
155
156 @property
157 def entity_id(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500158 return self._entity_id
159
160 @entity_id.setter
161 def entity_id(self, value):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500162 self._entity_id = value
163
164 @property
165 def encryption(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500166 return self._encryption
167
168 @encryption.setter
169 def encryption(self, value):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500170 # FIXME The encryption should come as boolean by default
171 value = eval(value)
172 assert isinstance(value, bool), 'encryption is a boolean'
173
174 if self._encryption != value:
175 self._encryption = value
176
177 @property
178 def discard_config(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500179 return self._discard_config
180
181 @discard_config.setter
182 def discard_config(self, discard_config):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500183 assert isinstance(discard_config, dict), "discard_config not dict"
184 assert 'max_probability' in discard_config, "max_probability missing"
185 assert 'max_threshold' in discard_config, "max_threshold missing"
186 assert 'min_threshold' in discard_config, "min_threshold missing"
187 self._discard_config = discard_config
188
189 @property
190 def discard_policy(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500191 return self._discard_policy
192
193 @discard_policy.setter
194 def discard_policy(self, discard_policy):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500195 dp = ("TailDrop", "WTailDrop", "RED", "WRED")
196 assert (isinstance(discard_policy, str))
197 assert (discard_policy in dp)
198 self._discard_policy = discard_policy
199
200 @property
201 def max_q_size(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500202 return self._max_q_size
203
204 @max_q_size.setter
205 def max_q_size(self, max_q_size):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500206 if isinstance(max_q_size, str):
207 assert (max_q_size == "auto")
208 else:
209 assert (isinstance(max_q_size, int))
210
211 self._max_q_size = max_q_size
212
213 @property
214 def pbit_map(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500215 return self._pbit_map
216
217 @pbit_map.setter
218 def pbit_map(self, pbit_map):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500219 assert (isinstance(pbit_map, str))
220 assert (len(pbit_map[2:]) == 8) # Example format of pbit_map: "0b00000101"
221 try:
222 _ = int(pbit_map[2], 2)
223 except ValueError:
224 raise Exception("pbit_map-not-binary-string-{}".format(pbit_map))
225
226 # remove '0b'
227 self._pbit_map = pbit_map[2:]
228
229 @property
230 def scheduling_policy(self):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500231 return self._scheduling_policy
232
233 @scheduling_policy.setter
234 def scheduling_policy(self, scheduling_policy):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500235 sp = ("WRR", "StrictPriority")
236 assert (isinstance(scheduling_policy, str))
237 assert (scheduling_policy in sp)
238 self._scheduling_policy = scheduling_policy
239
240 @staticmethod
241 def create(handler, gem_port):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500242
243 return OnuGemPort(gem_id=gem_port['gemport_id'],
244 uni_id=gem_port['uni_id'],
245 alloc_id=gem_port['alloc_id_ref'],
246 direction=gem_port['direction'],
247 encryption=gem_port['encryption'], # aes_indicator,
248 discard_config=gem_port['discard_config'],
249 discard_policy=gem_port['discard_policy'],
250 max_q_size=gem_port['max_q_size'],
251 pbit_map=gem_port['pbit_map'],
252 priority_q=gem_port['priority_q'],
253 scheduling_policy=gem_port['scheduling_policy'],
254 weight=gem_port['weight'],
255 handler=handler,
256 untagged=False)
257
258 @inlineCallbacks
259 def add_to_hardware(self, omci,
260 tcont_entity_id,
261 ieee_mapper_service_profile_entity_id,
262 gal_enet_profile_entity_id,
263 ul_prior_q_entity_id,
264 dl_prior_q_entity_id):
265
266 self.log.debug('add-to-hardware', entity_id=self.entity_id, gem_id=self.gem_id,
267 tcont_entity_id=tcont_entity_id,
268 ieee_mapper_service_profile_entity_id=ieee_mapper_service_profile_entity_id,
269 gal_enet_profile_entity_id=gal_enet_profile_entity_id,
270 ul_prior_q_entity_id=ul_prior_q_entity_id,
271 dl_prior_q_entity_id=dl_prior_q_entity_id)
272
273 try:
274 direction = "downstream" if self.multicast else "bi-directional"
275 assert not self.multicast, 'MCAST is not supported yet'
276
277 attributes = dict()
278 attributes['priority_queue_pointer_downstream'] = dl_prior_q_entity_id
279 msg = GemPortNetworkCtpFrame(
280 self.entity_id, # same entity id as GEM port
281 port_id=self.gem_id,
282 tcont_id=tcont_entity_id,
283 direction=direction,
284 upstream_tm=ul_prior_q_entity_id,
285 attributes=attributes
286 )
287 frame = msg.create()
288 self.log.debug('openomci-msg', omci_msg=msg)
289 results = yield omci.send(frame)
290 self.check_status_and_state(results, 'create-gem-port-network-ctp')
291
292 except Exception as e:
293 self.log.exception('gemport-create', e=e)
294 raise
295
296 try:
297 # TODO: magic numbers here
298 msg = GemInterworkingTpFrame(
299 self.entity_id, # same entity id as GEM port
300 gem_port_network_ctp_pointer=self.entity_id,
301 interworking_option=5, # IEEE 802.1
302 service_profile_pointer=ieee_mapper_service_profile_entity_id,
303 interworking_tp_pointer=0x0,
304 pptp_counter=1,
305 gal_profile_pointer=gal_enet_profile_entity_id
306 )
307 frame = msg.create()
308 self.log.debug('openomci-msg', omci_msg=msg)
309 results = yield omci.send(frame)
310 self.check_status_and_state(results, 'create-gem-interworking-tp')
311
312 except Exception as e:
313 self.log.exception('interworking-create', e=e)
314 raise
315
316 returnValue(results)
317
318 @inlineCallbacks
319 def remove_from_hardware(self, omci):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500320 self.log.debug('remove-from-hardware', gem_id=self.gem_id)
321
322 try:
323 msg = GemInterworkingTpFrame(self.entity_id)
324 frame = msg.delete()
325 self.log.debug('openomci-msg', omci_msg=msg)
326 results = yield omci.send(frame)
327 self.check_status_and_state(results, 'delete-gem-port-network-ctp')
328 except Exception as e:
329 self.log.exception('interworking-delete', e=e)
330 raise
331
332 try:
333 msg = GemPortNetworkCtpFrame(self.entity_id)
334 frame = msg.delete()
335 self.log.debug('openomci-msg', omci_msg=msg)
336 results = yield omci.send(frame)
337 self.check_status_and_state(results, 'delete-gem-interworking-tp')
338 except Exception as e:
339 self.log.exception('gemport-delete', e=e)
340 raise
341
342 returnValue(results)
343
344 def check_status_and_state(self, results, operation=''):
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500345 omci_msg = results.fields['omci_message'].fields
346 status = omci_msg['success_code']
347 error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
348 failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
349 unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
350
Matt Jeannerete8fc53e2019-04-13 15:58:33 -0400351 self.log.debug("OMCI Result", operation=operation, omci_msg=omci_msg,
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500352 status=status, error_mask=error_mask,
353 failed_mask=failed_mask, unsupported_mask=unsupported_mask)
354
355 if status == RC.Success:
356 return True
357
358 elif status == RC.InstanceExists:
359 return False