blob: 5ed28b2c6e46cd393ffebfa235d43c561aef82b8 [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
William Kurkian8235c1e2019-03-05 12:58:28 -050019from voltha_protos.common_pb2 import AdminState, OperStatus
20from voltha_protos.device_pb2 import Port
Matt Jeanneret72f96fc2019-02-11 10:53:05 -050021from pyvoltha.adapters.extensions.omci.tasks.task import Task
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050022
23BRDCM_DEFAULT_VLAN = 4091
24TASK_PRIORITY = Task.DEFAULT_PRIORITY + 10
25DEFAULT_TPID = 0x8100
26DEFAULT_GEM_PAYLOAD = 48
27
28
29class PonPort(object):
30 """Wraps northbound-port/ANI support for ONU"""
31 # TODO: possibly get from olt
32 MIN_GEM_ENTITY_ID = 0x408
33 MAX_GEM_ENTITY_ID = 0x4FF # TODO: This limits is internal to specific ONU. It should be more "discoverable"?
34
35 def __init__(self, handler, port_no):
36 self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
37 self.log.debug('function-entry')
38
39 self._enabled = False
40 self._valid = True
41 self._handler = handler
42 self._deferred = None
43 self._port = None
44 self._port_number = port_no
Matt Jeanneret0c287892019-02-28 11:48:00 -050045 self._peers = []
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050046 self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
47
48 self._admin_state = AdminState.ENABLED
49 self._oper_status = OperStatus.ACTIVE
50
51 self._gem_ports = {} # gem-id -> GemPort
52 self._tconts = {} # alloc-id -> TCont
53
54 self.ieee_mapper_service_profile_entity_id = 0x8001
55 self.mac_bridge_port_ani_entity_id = 0x2102 # TODO: can we just use the entity id from the anis list?
56
57 def __str__(self):
58 return "PonPort - port_number: {}, next_entity_id: {}, num_gem_ports: {}, num_tconts: {}".format(
59 self._port_number, self._next_entity_id, len(self._gem_ports), len(self._tconts))
60
61 def __repr__(self):
62 return str(self)
63
64 @staticmethod
65 def create(handler, port_no):
66 log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
67 log.debug('function-entry')
68 port = PonPort(handler, port_no)
69
70 return port
71
72 def _start(self):
73 self.log.debug('function-entry')
74 self._cancel_deferred()
75
76 self._admin_state = AdminState.ENABLED
77 self._oper_status = OperStatus.ACTIVE
78 self._update_adapter_agent()
79
80 def _stop(self):
81 self.log.debug('function-entry')
82 self._cancel_deferred()
83
84 self._admin_state = AdminState.DISABLED
85 self._oper_status = OperStatus.UNKNOWN
86 self._update_adapter_agent()
87
88 # TODO: stop h/w sync
89
90 def _cancel_deferred(self):
91 self.log.debug('function-entry')
92 d1, self._deferred = self._deferred, None
93
94 for d in [d1]:
95 try:
96 if d is not None and not d.called:
97 d.cancel()
98 except:
99 pass
100
101 def delete(self):
102 self.log.debug('function-entry')
103 self.enabled = False
104 self._valid = False
105 self._handler = None
106
107 @property
108 def enabled(self):
109 self.log.debug('function-entry')
110 return self._enabled
111
112 @enabled.setter
113 def enabled(self, value):
114 self.log.debug('function-entry')
115 if self._enabled != value:
116 self._enabled = value
117
118 if value:
119 self._start()
120 else:
121 self._stop()
122
123 @property
124 def port_number(self):
125 self.log.debug('function-entry')
126 return self._port_number
127
128 @property
129 def next_gem_entity_id(self):
130 self.log.debug('function-entry')
131 entity_id = self._next_entity_id
132
133 self._next_entity_id = self._next_entity_id + 1
134 if self._next_entity_id > PonPort.MAX_GEM_ENTITY_ID:
135 self._next_entity_id = PonPort.MIN_GEM_ENTITY_ID
136
137 return entity_id
138
139 @property
140 def tconts(self):
141 self.log.debug('function-entry')
142 return self._tconts
143
144 @property
145 def gem_ports(self):
146 self.log.debug('function-entry')
147 return self._gem_ports
148
149 def get_port(self):
150 """
151 Get the VOLTHA PORT object for this port
152 :return: VOLTHA Port object
153 """
154 self.log.debug('function-entry')
155
Matt Jeanneret0c287892019-02-28 11:48:00 -0500156 self._port = Port(port_no=self.port_number,
157 label='PON port',
158 type=Port.PON_ONU,
159 admin_state=self._admin_state,
160 oper_status=self._oper_status,
161 peers=self._peers)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500162 return self._port
163
Matt Jeanneret0c287892019-02-28 11:48:00 -0500164 def add_peer(self, parent_device_id, parent_port_no):
165 self.log.debug('add-peer-port', parent_device_id=parent_device_id, parent_port_no=parent_port_no)
166 new_peer = Port.PeerPort(device_id=parent_device_id, port_no=parent_port_no)
167 self._peers.extend([new_peer])
168
Matt Jeanneret84e56f62019-02-26 10:48:09 -0500169 @inlineCallbacks
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500170 def _update_adapter_agent(self):
171 """
172 Update the port status and state in the core
173 """
174 self.log.debug('function-entry')
175 self.log.debug('update-adapter-agent', admin_state=self._admin_state,
176 oper_status=self._oper_status)
177
178 if self._port is not None:
179 self._port.admin_state = self._admin_state
180 self._port.oper_status = self._oper_status
181
182 # adapter_agent add_port also does an update of port status
183 try:
Matt Jeannereta32441c2019-03-07 05:16:37 -0500184 yield self._handler.core_proxy.port_state_update(self._handler.device_id, self._port.type,
185 self._port.port_no,self._port.oper_status)
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -0500186 except Exception as e:
187 self.log.exception('update-port', e=e)
188
189 def add_tcont(self, tcont, reflow=False):
190 """
191 Creates/ a T-CONT with the given alloc-id
192
193 :param tcont: (TCont) Object that maintains the TCONT properties
194 :param reflow: (boolean) If true, force add (used during h/w resync)
195 :return: (deferred)
196 """
197 self.log.debug('function-entry', tcont=tcont.alloc_id)
198
199 if not self._valid:
200 return # Deleting
201
202 if not reflow and tcont.alloc_id in self._tconts:
203 return # already created
204
205 self.log.info('add-tcont', tcont=tcont.alloc_id, reflow=reflow)
206 self._tconts[tcont.alloc_id] = tcont
207
208 def update_tcont_td(self, alloc_id, new_td):
209 self.log.debug('function-entry')
210
211 tcont = self._tconts.get(alloc_id)
212
213 if tcont is None:
214 return # not-found
215
216 tcont.traffic_descriptor = new_td
217
218 # TODO: Not yet implemented
219 #TODO: How does this affect ONU tcont settings?
220 #try:
221 # results = yield tcont.add_to_hardware(self._handler.omci)
222 #except Exception as e:
223 # self.log.exception('tcont', tcont=tcont, e=e)
224 # # May occur with xPON provisioning, use hw-resync to recover
225 # results = 'resync needed'
226 # returnValue(results)
227
228 @inlineCallbacks
229 def remove_tcont(self, alloc_id):
230 self.log.debug('function-entry')
231
232 tcont = self._tconts.get(alloc_id)
233
234 if tcont is None:
235 returnValue('nop')
236
237 try:
238 del self._tconts[alloc_id]
239 results = yield tcont.remove_from_hardware(self._handler.openomci.omci_cc)
240 returnValue(results)
241
242 except Exception as e:
243 self.log.exception('delete', e=e)
244 raise
245
246 def gem_port(self, gem_id, direction):
247 self.log.debug('function-entry')
248 return self._gem_ports.get((gem_id, direction))
249
250 @property
251 def gem_ids(self):
252 """Get all GEM Port IDs used by this ONU"""
253 self.log.debug('function-entry')
254 return sorted([gem_id_and_direction[0] for gem_id_and_direction, gem in self._gem_ports.items()])
255
256 def add_gem_port(self, gem_port, reflow=False):
257 """
258 Add a GEM Port to this ONU
259
260 :param gem_port: (GemPort) GEM Port to add
261 :param reflow: (boolean) If true, force add (used during h/w resync)
262 :return: (deferred)
263 """
264 self.log.debug('function-entry', gem_port=gem_port.gem_id)
265
266 if not self._valid:
267 return # Deleting
268
269 if not reflow and (gem_port.gem_id, gem_port.direction) in self._gem_ports:
270 return # nop
271
272 # if this is actually a new gem port then issue the next entity_id
273 gem_port.entity_id = self.next_gem_entity_id
274 self.log.info('add-gem-port', gem_port=gem_port, reflow=reflow)
275 self._gem_ports[(gem_port.gem_id, gem_port.direction)] = gem_port
276
277 @inlineCallbacks
278 def remove_gem_id(self, gem_id, direction):
279 """
280 Remove a GEM Port from this ONU
281
282 :param gem_id: (GemPort) GEM Port to remove
283 :param direction: Direction of the gem port
284 :return: deferred
285 """
286 self.log.debug('function-entry', gem_id=gem_id)
287
288 gem_port = self._gem_ports.get((gem_id, direction))
289
290 if gem_port is None:
291 returnValue('nop')
292
293 try:
294 del self._gem_ports[(gem_id, direction)]
295 results = yield gem_port.remove_from_hardware(self._handler.openomci.omci_cc)
296 returnValue(results)
297
298 except Exception as ex:
299 self.log.exception('gem-port-delete', e=ex)
300 raise
301
302