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