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